如何使用 Spring Boot 和 Canal 實(shí)現(xiàn) MySQL 數(shù)據(jù)庫(kù)同步
前言
在分布式系統(tǒng)中,數(shù)據(jù)同步是一個(gè)常見(jiàn)的需求。例如,我們可能需要將主庫(kù)的數(shù)據(jù)實(shí)時(shí)同步到多個(gè)從庫(kù),或者將數(shù)據(jù)從一個(gè)數(shù)據(jù)庫(kù)集群同步到另一個(gè)集群。本篇內(nèi)容通過(guò)一個(gè)實(shí)際案例,介紹如何使用 Spring Boot 和 Canal 實(shí)現(xiàn) MySQL 數(shù)據(jù)庫(kù)之間的數(shù)據(jù)同步。
一、背景
假設(shè)我們有以下數(shù)據(jù)庫(kù)架構(gòu):
- 兩個(gè)主庫(kù):db_1 和 db_2。
- 每個(gè)主庫(kù)對(duì)應(yīng)兩個(gè)從庫(kù):db_1_bk_1、db_1_bk_2 和 db_2_bk_1、db_2_bk_2。
- 我們的目標(biāo)是:
- 將 db_1 的數(shù)據(jù)同步到 db_1_bk_1 和 db_1_bk_2。
- 將 db_2 的數(shù)據(jù)同步到 db_2_bk_1 和 db_2_bk_2。
二、Canal 簡(jiǎn)介
Canal 是阿里巴巴開(kāi)源的一款基于 MySQL Binlog 的增量數(shù)據(jù)訂閱與分發(fā)工具。它通過(guò)模擬 MySQL 的從節(jié)點(diǎn),實(shí)時(shí)捕獲主庫(kù)的 Binlog 日志,并將數(shù)據(jù)變更事件推送給下游消費(fèi)者。Canal 支持多種下游適配器,如 Kafka、RabbitMQ 和直接消費(fèi)。
三、主庫(kù)數(shù)據(jù)庫(kù)配置
1.主庫(kù)配置
為了使 Canal 能夠正常解析 Binlog 日志,主庫(kù)需要進(jìn)行以下配置:
- 開(kāi)啟 Binlog 日志:確保主庫(kù)開(kāi)啟了 Binlog 日志,并且設(shè)置為 ROW 模式。
- 配置 server-id:為每個(gè)主庫(kù)設(shè)置唯一的 server-id。
- 創(chuàng)建 Canal 用戶(hù)并授予權(quán)限:創(chuàng)建一個(gè)用戶(hù)供 Canal 使用,并授予必要的權(quán)限。
編輯主庫(kù)的配置文件(my.cnf 或 my.ini),添加以下內(nèi)容:
[mysqld] # 開(kāi)啟 Binlog 日志 log-bin=mysql-bin # 設(shè)置 Binlog 格式為 ROW 模式 binlog-format=ROW # 設(shè)置唯一的 server-id server-id=1
注意:
- 如果你有多個(gè)主庫(kù),每個(gè)主庫(kù)的 server-id 必須是唯一的。
- 修改配置后,需要重啟 MySQL 服務(wù)以使配置生效。
2.創(chuàng)建 Canal 用戶(hù)并授予權(quán)限
Canal 需要一個(gè)具有讀取 Binlog 權(quán)限的 MySQL 用戶(hù)。以下是創(chuàng)建用戶(hù)并授予權(quán)限的步驟:
# 登錄 MySQL mysql -u root -p # 創(chuàng)建用戶(hù) CREATE USER 'canal'@'%' IDENTIFIED BY 'canal'; # 授予權(quán)限 GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%' IDENTIFIED BY 'canal'; # 刷新權(quán)限 FLUSH PRIVILEGES;
說(shuō)明:
- canal 用戶(hù)需要足夠的權(quán)限來(lái)讀取 Binlog 數(shù)據(jù),但不需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行寫(xiě)操作。
- 如果你的 MySQL 版本較新(8.x),可能需要使用 ALTER USER 命令來(lái)設(shè)置密碼:
ALTER USER 'canal'@'%' IDENTIFIED BY 'canal';
四.配置 Canal Server
Canal Server 是 Canal 的核心組件,負(fù)責(zé)連接主庫(kù)并解析 Binlog 數(shù)據(jù)。我們需要為每個(gè)主庫(kù)配置一個(gè) Canal 實(shí)例。
1.Canal Server 配置文件
在 Canal Server 的配置目錄下,創(chuàng)建兩個(gè)實(shí)例配置文件:conf/db_1/instance.properties 和 conf/db_2/instance.properties。conf/db_1/instance.properties:
# 主庫(kù)的地址和端口 canal.instance.master.address=db_1_ip:3306 # Canal 連接主庫(kù)的用戶(hù)名和密碼 canal.instance.dbUsername=canal canal.instance.dbPassword=canal # 需要同步的表正則表達(dá)式,這里表示同步 db_1 數(shù)據(jù)庫(kù)的所有表 canal.instance.filter.regex=db_1\\..*
conf/db_2/instance.properties:
# 主庫(kù)的地址和端口 canal.instance.master.address=db_2_ip:3306 # Canal 連接主庫(kù)的用戶(hù)名和密碼 canal.instance.dbUsername=canal canal.instance.dbPassword=canal # 需要同步的表正則表達(dá)式,這里表示同步 db_2 數(shù)據(jù)庫(kù)的所有表 canal.instance.filter.regex=db_2\\..*
2.啟動(dòng) Canal Server
使用以下命令啟動(dòng) Canal Server:
nohup sh bin/canal.sh start &
注意:
- 確保主庫(kù)的 Binlog 位置和文件名正確。如果不確定,可以通過(guò) SHOW MASTER STATUS; 命令查看。
- 如果主庫(kù)已經(jīng)運(yùn)行了一段時(shí)間,需要指定 Binlog 的起始位置,避免重復(fù)同步舊數(shù)據(jù)。
五.開(kāi)發(fā) Spring Boot 客戶(hù)端
Spring Boot 客戶(hù)端作為 Canal 的消息消費(fèi)者,負(fù)責(zé)接收數(shù)據(jù)變更事件并同步到目標(biāo)從庫(kù)。
1. 引入依賴(lài)
在 Spring Boot 項(xiàng)目的 pom.xml文件中,引入 Canal 客戶(hù)端依賴(lài):
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.8</version>
</dependency>2. 配置 Canal 客戶(hù)端
在 application.yml 文件中,配置 Canal Server 的地址:
canal: server.ip: canal_server_ip server.port: 11111
3. 實(shí)現(xiàn)數(shù)據(jù)同步邏輯
創(chuàng)建一個(gè) Canal 客戶(hù)端服務(wù)類(lèi),用于接收和處理數(shù)據(jù)變更事件。CanalClientService.java:
@Service
public class CanalClientService {
private final CanalConnector canalConnector;
public CanalClientService(@Value("${canal.server.ip}") String canalServerIp, @Value("${canal.server.port}") int canalServerPort) {
this.canalConnector = CanalConnectors.newSingleConnector(new InetSocketAddress(canalServerIp, canalServerPort), "example", "", "");
}
@PostConstruct
public void start() {
canalConnector.connect();
canalConnector.subscribe("db_1..*, db_2..*"); // 訂閱 db_1 和 db_2 的所有表
new Thread(this::process).start();
}
private void process() {
while (true) {
Message message = canalConnector.getWithoutAck(100);
long batchId = message.getId();
if (batchId == -1 || message.getEntries().isEmpty()) {
continue;
}
for (Entry entry : message.getEntries()) {
handleData(entry);
}
canalConnector.ack(batchId);
}
}
private void handleData(Entry entry) {
String schemaName = entry.getHeader().getSchemaName(); // 數(shù)據(jù)庫(kù)名
String tableName = entry.getHeader().getTableName(); // 表名
EventType eventType = entry.getHeader().getEventType(); // 數(shù)據(jù)變更類(lèi)型
System.out.println("Schema: " + schemaName + ", Table: " + tableName + ", Type: " + eventType);
// 根據(jù)來(lái)源數(shù)據(jù)庫(kù)同步到對(duì)應(yīng)的從庫(kù)
if ("db_1".equals(schemaName)) {
syncToBackupDbs(entry, "db_1_bk_1", "db_1_bk_2");
} else if ("db_2".equals(schemaName)) {
syncToBackupDbs(entry, "db_2_bk_1", "db_2_bk_2");
}
}
private void syncToBackupDbs(Entry entry, String... backupDbs) {
// 根據(jù)事件類(lèi)型同步到從庫(kù)
if (entry.getHeader().getEventType() == EventType.INSERT) {
for (String db : backupDbs) {
syncInsert(entry, db);
}
} else if (entry.getHeader().getEventType() == EventType.UPDATE) {
for (String db : backupDbs) {
syncUpdate(entry, db);
}
} else if (entry.getHeader().getEventType() == EventType.DELETE) {
for (String db : backupDbs) {
syncDelete(entry, db);
}
}
}
private void syncInsert(Entry entry, String backupDb) {
// 使用 MyBatis 將數(shù)據(jù)插入到對(duì)應(yīng)的從庫(kù)
System.out.println("INSERT into " + backupDb);
}
private void syncUpdate(Entry entry, String backupDb) {
// 使用 MyBatis 將數(shù)據(jù)更新到對(duì)應(yīng)的從庫(kù)
System.out.println("UPDATE into " + backupDb);
}
private void syncDelete(Entry entry, String backupDb) {
// 使用 MyBatis 將數(shù)據(jù)從對(duì)應(yīng)的從庫(kù)刪除
System.out.println("DELETE from " + backupDb);
}
}六.啟動(dòng)并測(cè)試
- 啟動(dòng) Canal Server。
- 啟動(dòng) Spring Boot 應(yīng)用。
- 在主庫(kù) db_1 或 db_2 中插入、更新或刪除數(shù)據(jù)。
- 觀察從庫(kù) db_1_bk_1、db_1_bk_2、db_2_bk_1 和 db_2_bk_2 是否同步成功。
七.注意事項(xiàng)
- 數(shù)據(jù)一致性:確保從庫(kù)的數(shù)據(jù)與主庫(kù)保持一致。可以通過(guò)事務(wù)或鎖機(jī)制來(lái)避免沖突。
- 性能優(yōu)化:如果數(shù)據(jù)量較大,建議結(jié)合中間件(如 Kafka)進(jìn)行緩沖和負(fù)載均衡。
- 錯(cuò)誤處理:在同步過(guò)程中,需要處理網(wǎng)絡(luò)異常、數(shù)據(jù)庫(kù)連接異常等情況。
- Canal Server 高可用:在生產(chǎn)環(huán)境中,建議部署 Canal Server 的集群,以提高系統(tǒng)的可用性。
八.總結(jié)
通過(guò) Spring Boot 和 Canal,我們可以實(shí)現(xiàn) MySQL 數(shù)據(jù)庫(kù)之間的高效數(shù)據(jù)同步。Canal 提供了強(qiáng)大的 Binlog 解析能力,而 Spring Boot 則提供了靈活的開(kāi)發(fā)框架,兩者結(jié)合可以輕松應(yīng)對(duì)復(fù)雜的分布式數(shù)據(jù)同步需求。希望本文對(duì)你有所幫助,如果有任何問(wèn)題,歡迎在評(píng)論區(qū)留言。
到此這篇關(guān)于使用 Spring Boot 和 Canal 實(shí)現(xiàn) MySQL 數(shù)據(jù)庫(kù)同步的文章就介紹到這了,更多相關(guān)Spring Boot MySQL 數(shù)據(jù)庫(kù)同步內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java并發(fā)編程之LockSupport類(lèi)詳解
LockSupport是一種線(xiàn)程阻塞工具,它可以在線(xiàn)程內(nèi)任意位置讓線(xiàn)程阻塞.接下來(lái)就帶著大家詳細(xì)了解一下LockSupport類(lèi),,需要的朋友可以參考下2021-05-05
java仿Servlet生成驗(yàn)證碼實(shí)例詳解
這篇文章主要介紹了java仿Servlet生成驗(yàn)證碼實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04
Java學(xué)習(xí)常用包(類(lèi))之java.util包詳解
這篇文章主要介紹了Java學(xué)習(xí)常用包(類(lèi))之java.util包的相關(guān)資料,Java.util包是Java標(biāo)準(zhǔn)類(lèi)庫(kù)的重要組成部分,包含集合框架、日期時(shí)間類(lèi)、事件模型、隨機(jī)數(shù)生成器等實(shí)用工具類(lèi),集合框架提供了多種數(shù)據(jù)結(jié)構(gòu)和算法,需要的朋友可以參考下2024-10-10
使用SpringBoot簡(jiǎn)單實(shí)現(xiàn)一個(gè)蘋(píng)果支付的場(chǎng)景
這篇文章主要為大家詳細(xì)介紹了如何在Spring?Boot項(xiàng)目中集成Apple?Pay功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11
JAVA之讀取properties時(shí)路徑的注意問(wèn)題
這篇文章主要介紹了JAVA之讀取properties時(shí)路徑的注意問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
JVisualVM之Java性能監(jiān)控與調(diào)優(yōu)利器詳解
本文將詳細(xì)介紹JVisualVM的使用方法,并結(jié)合實(shí)際案例展示如何利用它進(jìn)行性能調(diào)優(yōu),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-05-05
java中List去除重復(fù)數(shù)據(jù)的5種方式總結(jié)
這篇文章主要給大家總結(jié)介紹了關(guān)于java中List去除重復(fù)數(shù)據(jù)的5種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Java使用easyExcel批量導(dǎo)入數(shù)據(jù)詳解
這篇文章主要介紹了Java使用easyExcel批量導(dǎo)入數(shù)據(jù)詳解,通常我們會(huì)提供一個(gè)模板,此模塊我們可以使用easyExcel導(dǎo)出數(shù)據(jù)生成的一個(gè)Excel文件當(dāng)作模板,提供下載鏈接,用戶(hù)在該文件內(nèi)填入規(guī)定的數(shù)據(jù)格式以后可以批量導(dǎo)入數(shù)據(jù)到數(shù)據(jù)庫(kù)中,需要的朋友可以參考下2023-08-08

