如何通過Java監(jiān)聽MySQL數(shù)據(jù)的變化
原理
原理:java通過bin-log監(jiān)控mysql數(shù)據(jù)變化
binlog :binlog 就是binary log,二進(jìn)制日志文件,這個(gè)文件記錄了mysql所有的增、刪、改語句。通過binlog日志我們可以做數(shù)據(jù)恢復(fù),做主從復(fù)制等等??梢钥吹剑灰辛诉@個(gè)binlog,我們就擁有了mysql的完整備份了。
就是說一旦開啟了這個(gè)功能,數(shù)據(jù)庫中數(shù)據(jù)的任何變化,都會記錄到這種日志文件中,所以可以通過Java監(jiān)聽這種的文件,來監(jiān)聽MySQL數(shù)據(jù)的變化。
開啟MySQL的binlog功能
首先,需要開啟MySQL的binlog功能,MySQL默認(rèn)是未開啟的
查詢是否開始binlog功能的sql語句:show VARIABLES like '%log_bin%';
log_bin的值是on則表示開啟,我也不知道沒開啟是沒有這個(gè)還是值是off,我也不敢瞎說
開啟分為兩步,1.改配置 2.重啟MySQL服務(wù)
改配置
打開自己MySQL的配置文件,比如PC端的即my.ini文件,加上下面三句
log-bin=mysql-bin #[必須]啟用二-進(jìn)制日志 server-id=100 #[必須]服務(wù)器唯一ID,如果是用于多臺MySQL,ID不要重復(fù)就行 binlog-format = ROW #這句也得加,下面解釋
改完,然后重啟MySQL服務(wù)就可以了
說明:
mysql-bin只是個(gè)名字而已,可以隨便起。將來保存的日志文件名就是mysql-bin.000001,mysql-bin.000002這樣的。生成的日志文件會存放在自己的MySQL的存放數(shù)據(jù)的文件夾,比如我設(shè)置的存放MySQL數(shù)據(jù)的文件夾目錄是:D:\MySql\mysql-8.0.24\data\
,然后生成的日志文件就會在這個(gè)目錄下。
binlog-format = ROW,binlog_format 設(shè)置為 ROW可以保證數(shù)據(jù)的一致, 因?yàn)樵?STATEMENT 或 MIXED 模式下, Binlog 只會記錄和傳輸 SQL 語句(以減少日志大小),而不包含具體數(shù)據(jù),我們也就無法保存了。
Java監(jiān)聽MySQL的binlog實(shí)現(xiàn)監(jiān)聽數(shù)據(jù)變化
Java需要使用一個(gè)工具mysql-binlog-connector-java
導(dǎo)入Jar包:
這個(gè)Jar包有兩個(gè)版本,一個(gè)是com.github.shyiko的一個(gè)是com.zendesk,使用方式和代碼完全一樣,只是各自的實(shí)現(xiàn)方式可能不同,但是com.github.shyiko20年停止更新了,對于MySQL8兼容性不是很好,建議使用com.zendesk。
<dependency> <groupId>com.zendesk</groupId> <artifactId>mysql-binlog-connector-java</artifactId> <version>0.27.1</version> <!--2022.09.17版的--> </dependency>
順便附上com.github.shyiko
的
MySQL8及以上使用com.github.shyiko的可能會出現(xiàn)Client does not support authentication protocol requested by server...
錯(cuò)誤
原因:mysql8 之前的版本中加密規(guī)則是mysql_native_password,而在mysql8之后,加密規(guī)則是caching_sha2_password,
解決辦法:把mysql用戶登錄密碼加密規(guī)則還原成mysql_native_password,
SQL語句:ALTER USER 'root'@'自己需要連接的數(shù)據(jù)庫的host,自己本機(jī)的就用localhost' IDENTIFIED WITH mysql_native_password BY '自己所使用的登錄密碼';
然后就好了
然后再看邏輯代碼:
實(shí)現(xiàn)是在connectMysqlBinLog()方法中的,里面通過data instanceof ****,即可查詢到執(zhí)行的增刪改什么請求,詳細(xì)請看代碼,看一下就明白了
//為什么甚至路徑都一樣,還是com.github.shyiko.***, // 因?yàn)閏om.zendesk這個(gè)包,里面包了個(gè)com.github.shyiko.***這玩意,我懷疑是收購關(guān)系 import com.github.shyiko.mysql.binlog.BinaryLogClient; import com.github.shyiko.mysql.binlog.event.*; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; import java.io.IOException; //此類可以監(jiān)控MySQL庫數(shù)據(jù)的增刪改 @Component @Slf4j //用于打印日志 //在SpringBoot中,提供了一個(gè)接口:ApplicationRunner。 //該接口中,只有一個(gè)run方法,他執(zhí)行的時(shí)機(jī)是:spring容器啟動完成之后,就會緊接著執(zhí)行這個(gè)接口實(shí)現(xiàn)類的run方法。 public class MysqlBinLogClient implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { //項(xiàng)目啟動完成連接bin-log new Thread(() -> { connectMysqlBinLog(); }).start(); } /** * 連接mysqlBinLog */ public void connectMysqlBinLog() { log.info("監(jiān)控BinLog服務(wù)已啟動"); //自己MySQL的信息。host,port,username,password BinaryLogClient client = new BinaryLogClient("localhost", 3306, "root", "root"); /**因?yàn)閎inlog不是以數(shù)據(jù)庫為單位劃分的,所以監(jiān)控binglog不是監(jiān)控的單個(gè)的數(shù)據(jù)庫,而是整個(gè)當(dāng)前所設(shè)置連接的MySQL, *其中任何一個(gè)庫發(fā)生數(shù)據(jù)增刪改,這里都能檢測到, *所以不用設(shè)置所監(jiān)控的數(shù)據(jù)庫的名字(我也不知道怎么設(shè)置,沒發(fā)現(xiàn)有包含這個(gè)形參的構(gòu)造函數(shù)) *如果需要只監(jiān)控指定的數(shù)據(jù)庫,可以看后面代碼,可以獲取到當(dāng)前發(fā)生變更的數(shù)據(jù)庫名稱。可以根據(jù)名稱來決定是否監(jiān)控 **/ client.setServerId(100); //和自己之前設(shè)置的server-id保持一致,但是我不知道為什么不一致也能成功 //下面直接照抄就行 client.registerEventListener(event -> { EventData data = event.getData(); if (data instanceof TableMapEventData) { //只要連接的MySQL發(fā)生的增刪改的操作,則都會進(jìn)入這里,無論哪個(gè)數(shù)據(jù)庫 TableMapEventData tableMapEventData = (TableMapEventData) data; //可以通過轉(zhuǎn)成TableMapEventData類實(shí)例的tableMapEventData來獲取當(dāng)前發(fā)生變更的數(shù)據(jù)庫 System.out.println("發(fā)生變更的數(shù)據(jù)庫:"+tableMapEventData.getDatabase()); System.out.print("TableID:"); //表ID System.out.println(tableMapEventData.getTableId()); System.out.print("TableName:"); //表名字 System.out.println(tableMapEventData.getTable()); } //表數(shù)據(jù)發(fā)生修改時(shí)觸發(fā) if (data instanceof UpdateRowsEventData) { System.out.println("Update:"); System.out.println(data.toString()); //表數(shù)據(jù)發(fā)生插入時(shí)觸發(fā) } else if (data instanceof WriteRowsEventData) { System.out.println("Insert:"); System.out.println(data.toString()); //表數(shù)據(jù)發(fā)生刪除后觸發(fā) } else if (data instanceof DeleteRowsEventData) { System.out.println("Delete:"); System.out.println(data.toString()); } }); try { client.connect(); } catch (IOException e) { e.printStackTrace(); } } }
有一定可能數(shù)據(jù)發(fā)生變更后控制臺沒打印,可能是加載慢的原因,稍等一會控制臺就可能會打印相關(guān)信息
總結(jié)
到此這篇關(guān)于如何通過Java監(jiān)聽MySQL數(shù)據(jù)的變化的文章就介紹到這了,更多相關(guān)Java監(jiān)聽MySQL數(shù)據(jù)變化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis使用注解開發(fā)實(shí)現(xiàn)過程詳解
這篇文章主要介紹了MyBatis使用注解開發(fā)實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03List集合按某個(gè)屬性或者字段進(jìn)行分組的操作
這篇文章主要介紹了List集合按某個(gè)屬性或者字段進(jìn)行分組的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Java后端限制頻繁請求和重復(fù)提交的實(shí)現(xiàn)
很多用戶會請求過于頻繁或者是多次重復(fù)提交數(shù)據(jù),本文主要介紹了Java后端限制頻繁請求和重復(fù)提交的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04SpringBoot使用JDBC獲取相關(guān)的數(shù)據(jù)方法
這篇文章主要介紹了SpringBoot使用JDBC獲取相關(guān)的數(shù)據(jù)方法,JDBC與數(shù)據(jù)庫建立連接、發(fā)送 操作數(shù)據(jù)庫的語句并處理結(jié)果,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03Spring?Cloud負(fù)載均衡組件Ribbon原理解析
本文主要講述了微服務(wù)體系下的?Spring?Cloud?Netflix?套件中?Ribbon?的使用,并結(jié)合部分源碼講述了?Ribbon?的底層原理,重點(diǎn)講述了?Ribbon?中是如何獲取服務(wù)以及如何判定一個(gè)服務(wù)是否可用,最后也介紹了?Ribbon?中默認(rèn)提供的?7?種負(fù)載均衡策略,感興趣的朋友一起看看吧2022-04-04如何基于java實(shí)現(xiàn)解壓ZIP TAR等文件
這篇文章主要介紹了如何基于java實(shí)現(xiàn)解壓ZIP TAR等文件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07