前提
安裝完MySQL(我安裝的是5.7),安裝JDK(canal依賴)
開啟MySQL的binlog
開啟binlog,並且將binlog的格式改為Row,這樣就可以獲取到CURD的二進位制內容。配置/etc/my.cnf,在[mysqld]增加
log-bin=mysql-bin #新增這一行就ok
binlog-format=ROW #選擇row模式
server_id=1 # 唯一,不能和其他叢集MySQL的server_id一樣
複製程式碼
驗證binlog是否開啟
登入MySQL,使用命令:
show variables like 'log_%';
複製程式碼
若 log_bin顯示為 on ,則說明開啟。
給canal分配MySQL的賬號
給canal分配一個MySQL的賬號,方便canal偷取MySQL的binlog。
CREATE USER canal IDENTIFIED BY 'canal';
GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
複製程式碼
檢視是否給canal賬號分配許可權
show grants for 'canal'
複製程式碼
下載解壓canal
地址:github.com/alibaba/can… ,目前穩定版是 v1.1.0,下載 canal.deployer-1.1.0.tar.gz。解壓到 canal目錄下(沒有該目錄 就新建)
注:canal 是純Java寫的,所有需要依賴JDK環境,我這邊使用的是:1.8.0_65-b17
# 下載
wget https://github.com/alibaba/canal/releases/download/canal-1.1.0/canal.deployer-1.1.0.tar.gz
# 建立canal目錄
mkdir canal
# 解壓
tar -zxvf https://github.com/alibaba/canal/releases/download/canal-1.1.0/canal.deployer-1.1.0.tar.gz
複製程式碼
canal和instance配置檔案
一個canal裡面可能會有多個instance,也就說一個instance可以監控一個mysql例項,多個instance也就可以對應多臺伺服器的mysql例項。也就是一個canal就可以監控分庫分表下的多機器MySQL。
(1)canal.properties
canal/config 中的canal.properties檔案,是全域性性的canal伺服器配置 ,修改內容如下:
#################################################
######### common argument #############
#################################################
# id唯一,不可與mysql的server_id重複
canal.id= 2
canal.ip=
canal.port=11111
canal.metrics.pull.port=11112
canal.zkServers=
# flush data to zk
canal.zookeeper.flush.period = 1000
canal.withoutNetty = false
# flush meta cursor/parse position to file
canal.file.data.dir = ${canal.conf.dir}
canal.file.flush.period = 1000
## memory store RingBuffer size, should be Math.pow(2,n)
canal.instance.memory.buffer.size = 16384
## memory store RingBuffer used memory unit size , default 1kb
canal.instance.memory.buffer.memunit = 1024
## meory store gets mode used MEMSIZE or ITEMSIZE
canal.instance.memory.batch.mode = MEMSIZE
## detecing config
canal.instance.detecting.enable = false
#canal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now()
canal.instance.detecting.sql = select 1
canal.instance.detecting.interval.time = 3
canal.instance.detecting.retry.threshold = 3
canal.instance.detecting.heartbeatHaEnable = false
# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery
canal.instance.transaction.size = 1024
# mysql fallback connected to new master should fallback times
canal.instance.fallbackIntervalInSeconds = 60
# network config
canal.instance.network.receiveBufferSize = 16384
canal.instance.network.sendBufferSize = 16384
canal.instance.network.soTimeout = 30
# binlog filter config
canal.instance.filter.druid.ddl = true
canal.instance.filter.query.dcl = false
canal.instance.filter.query.dml = false
canal.instance.filter.query.ddl = false
canal.instance.filter.table.error = false
canal.instance.filter.rows = false
canal.instance.filter.transaction.entry = false
# binlog format/image check
canal.instance.binlog.format = ROW,STATEMENT,MIXED
canal.instance.binlog.image = FULL,MINIMAL,NOBLOB
# binlog ddl isolation
canal.instance.get.ddl.isolation = false
# parallel parser config
canal.instance.parser.parallel = true
## concurrent thread number, default 60% available processors, suggest not to exceed Runtime.getRuntime().availableProcessors()
# parallelThreadSize預設是註釋掉的,原值為16,因為canal裝在本地VM上,分配了1個CPU,導致報錯,改為1
canal.instance.parser.parallelThreadSize = 1
## disruptor ringbuffer size, must be power of 2
canal.instance.parser.parallelBufferSize = 256
# table meta tsdb info
canal.instance.tsdb.enable=true
canal.instance.tsdb.dir=${canal.file.data.dir:../conf}/${canal.instance.destination:}
canal.instance.tsdb.url=jdbc:h2:${canal.instance.tsdb.dir}/h2;CACHE_SIZE=1000;MODE=MYSQL;
canal.instance.tsdb.dbUsername=canal
canal.instance.tsdb.dbPassword=canal
# rds oss binlog account
canal.instance.rds.accesskey =
canal.instance.rds.secretkey =
#################################################
######### destinations #############
#################################################
canal.destinations= example
# conf root dir
canal.conf.dir = ../conf
# auto scan instance dir add/remove and start/stop instance
canal.auto.scan = true
canal.auto.scan.interval = 5
canal.instance.tsdb.spring.xml=classpath:spring/tsdb/h2-tsdb.xml
#canal.instance.tsdb.spring.xml=classpath:spring/tsdb/mysql-tsdb.xml
canal.instance.global.mode = spring
canal.instance.global.lazy = false
#canal.instance.global.manager.address = 127.0.0.1:1099
#canal.instance.global.spring.xml = classpath:spring/memory-instance.xml
canal.instance.global.spring.xml = classpath:spring/file-instance.xml
#canal.instance.global.spring.xml = classpath:spring/default-instance.xml
# position info,需要改成自己的資料庫資訊
canal.instance.master.address = 127.0.0.1:3306
canal.instance.master.journal.name =
canal.instance.master.position =
canal.instance.master.timestamp =
# username/password,需要改成自己的資料庫資訊
canal.instance.dbUsername = canal
canal.instance.dbPassword = canal
canal.instance.defaultDatabaseName = test
canal.instance.connectionCharset = UTF-8
# table regex
canal.instance.filter.regex = .*\\..*
複製程式碼
(2)instance.properties
位於 canal/example/instance.properties,是具體的某個instances例項的配置,未涉及到的配置都會從canal.properties上繼承,內容如下:
#################################################
## mysql serverId , v1.0.26+ will autoGen
# canal.instance.mysql.slaveId=0
# enable gtid use true/false
canal.instance.gtidon=false
# position info address修改為自己的mysql地址
canal.instance.master.address=192.168.204.128:3306
canal.instance.master.journal.name=
canal.instance.master.position=
canal.instance.master.timestamp=
canal.instance.master.gtid=
# rds oss binlog
canal.instance.rds.accesskey=
canal.instance.rds.secretkey=
canal.instance.rds.instanceId=
# table meta tsdb info
canal.instance.tsdb.enable=true
#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb
#canal.instance.tsdb.dbUsername=canal
#canal.instance.tsdb.dbPassword=canal
#canal.instance.standby.address =
#canal.instance.standby.journal.name =
#canal.instance.standby.position =
#canal.instance.standby.timestamp =
#canal.instance.standby.gtid=
# username/password 修改為在mysql中給canal同步資料的賬號 密碼
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
# 監聽的資料庫
canal.instance.defaultDatabaseName=test
canal.instance.connectionCharset=UTF-8
# table regex
canal.instance.filter.regex=.*\\..*
# table black regex
canal.instance.filter.black.regex=
#################################################
複製程式碼
建立test資料庫
檢視MySQL上是否有test資料庫,沒有則建立
開啟canal
進入canal/bin,執行:./startup.sh。
使用 ps -ef|grep canal 驗證是否開啟。
Java client程式碼
建立SpringBoot工程,引入依賴:
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.0</version>
</dependency>
複製程式碼
建立TestCanal類:
package com.xbq.canal.test;
import java.awt.Event;
import java.net.InetSocketAddress;
import java.util.List;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
import com.alibaba.otter.canal.protocol.CanalEntry.Header;
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.InvalidProtocolBufferException;
/**
* @Auther: xbq
* @Date: 2018/9/11 19:16
* @Description:
*/
public class TestCanal {
public static void main(String[] args) throws InterruptedException {
// 第一步:與canal進行連線
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.204.128", 11111),
"example", "", "");
connector.connect();
// 第二步:開啟訂閱
connector.subscribe();
// 第三步:迴圈訂閱
while (true) {
try {
// 每次讀取 1000 條
Message message = connector.getWithoutAck(1000);
long batchID = message.getId();
int size = message.getEntries().size();
if (batchID == -1 || size == 0) {
System.out.println("當前暫時沒有資料");
Thread.sleep(1000);
} else {
System.out.println("-------------------------- 有資料啦 -----------------------");
PrintEntry(message.getEntries());
}
// position id ack (方便處理下一條)
connector.ack(batchID);
} catch (Exception e) {
// TODO: handle exception
} finally {
Thread.sleep(1000);
}
}
}
/**
* 獲取每條列印的記錄
* @param entrys
*/
public static void PrintEntry(List<Entry> entrys) {
for (Entry entry : entrys) {
// 第一步:拆解entry 實體
Header header = entry.getHeader();
EntryType entryType = entry.getEntryType();
// 第二步: 如果當前是RowData,那就是我需要的資料
if (entryType == EntryType.ROWDATA) {
String tableName = header.getTableName();
String schemaName = header.getSchemaName();
RowChange rowChange = null;
try {
rowChange = RowChange.parseFrom(entry.getStoreValue());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
EventType eventType = rowChange.getEventType();
System.out.println(String.format("當前正在操作 %s.%s, Action= %s", schemaName, tableName, eventType));
// 如果是‘查詢’ 或者 是 ‘DDL’ 操作,那麼sql直接打出來
if (eventType == EventType.QUERY || rowChange.getIsDdl()) {
System.out.println("rowchange sql ----->" + rowChange.getSql());
return;
}
// 第三步:追蹤到 columns 級別
rowChange.getRowDatasList().forEach((rowData) -> {
// 獲取更新之前的column情況
List<Column> beforeColumns = rowData.getBeforeColumnsList();
// 獲取更新之後的 column 情況
List<Column> afterColumns = rowData.getAfterColumnsList();
// 當前執行的是 刪除操作
if (eventType == EventType.DELETE) {
PrintColumn(beforeColumns);
}
// 當前執行的是 插入操作
if (eventType == EventType.INSERT) {
PrintColumn(afterColumns);
}
// 當前執行的是 更新操作
if (eventType == EventType.UPDATE) {
PrintColumn(afterColumns);
}
});
}
}
}
/**
* 每個row上面的每一個column 的更改情況
* @param columns
*/
public static void PrintColumn(List<Column> columns) {
columns.forEach((column) -> {
String columnName = column.getName();
String columnValue = column.getValue();
String columnType = column.getMysqlType();
// 判斷 該欄位是否更新
boolean isUpdated = column.getUpdated();
System.out.println(String.format("columnName=%s, columnValue=%s, columnType=%s, isUpdated=%s", columnName,
columnValue, columnType, isUpdated));
});
}
}
複製程式碼
執行此類。在MySQL test資料庫中建立student表,對其進行增刪改,可以發現控制檯上列印:有資料庫啦……
參考
[快取一致性和跨伺服器查詢的資料異構解決方案canal
歡迎關注我的公眾號~ 搜尋公眾號: 翻身碼農把歌唱 或者 掃描下方二維碼: