1,Canal 組件部署
- 安裝配置MySQL
1.1 安裝 mysql囊陡,
1.2 配置 mysql binlog使用ROW模式
1.3 在MySQL添加對應(yīng)的canal用戶
1.4 檢查canal用戶生效- 下載canal并配置
2.1 下載canal(1.1.4)
2.2 配置 canal(1.1.4)
2.3 啟動canal (需要JDK>=1.6.25)
(1) 配置MySQL
(1.1) 安裝MySQL
(1.2) 修改MySQL配置文件
canal的原理是基于mysql binlog技術(shù)纽乱,所以需要開啟mysql的binlog寫入功能蛾绎,并且配置binlog模式為row.
[mysqld]
log-bin=mysql-bin # 開啟 binlog
binlog-format=ROW # 選擇 ROW 模式
server_id=1 # 配置 MySQL replaction ,不能和 canal 的 slaveId 重復(fù)
(1.3) MySQL添加canal用戶并授權(quán)
canal的原理是模擬自己為mysql slave迫淹,所以需要mysql slave的相關(guān)權(quán)限
CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
(1.4) 校驗(yàn)用戶對應(yīng)權(quán)限
show master status ;
如果正常顯示binlog秘通,則沒問題,如果提示Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation
敛熬,則沒有對應(yīng) REPLICATION CLIENT 權(quán)限
show slave status ;
如果提示Access denied; you need (at least one of) the REPLICATION SLAVE privilege(s) for this operation
,則沒有對應(yīng) REPLICATION SLAVE 權(quán)限
(2) 下載并啟動canal
執(zhí)行 ./bin/startup.sh
即可啟動
(2.1) 下載canal
到 https://github.com/alibaba/canal/releases 選擇合適的版本
下載 wget https://github.com/alibaba/canal/releases/download/canal-1.1.14/canal.deployer-1.1.14.tar.gz
(2.2) 修改配置
修改
conf/example/instance.properties
以下只列出比較重要的配置
詳細(xì)的參數(shù)的說明可以參考這個(gè)網(wǎng)址
## mysql serverId 不能重復(fù)
canal.instance.mysql.slaveId = 1234
#position info第股,需要改成自己的數(shù)據(jù)庫信息
canal.instance.master.address = 127.0.0.1:3306
#username/password应民,需要改成自己的數(shù)據(jù)庫信息
canal.instance.dbUsername = canal
canal.instance.dbPassword = canal
canal.instance.defaultDatabaseName = database_wkq
canal.instance.connectionCharset = UTF-8
#table regex 需要監(jiān)控的表 通過,分隔 也可以使用正則 .*\\..*
canal.instance.filter.regex = table_wkq,table_2,table_3
# table black regex
canal.instance.filter.black.regex =
(2.3) 啟動canal
通過
sh bin/startup.sh
或者./bin/startup.sh
啟動
啟動后通過
jps -l
命令 可以看到com.alibaba.otter.canal.deployer.CanalLauncher
canal啟動時(shí)canal.log
canal.deployer-1.0.24/logs/canal/canal.log
2018-07-23 20:27:46.449 [main] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## start the canal server.
2018-07-23 20:27:46.625 [main] INFO com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[10.0.62.130:11111]
2018-07-23 20:27:47.576 [main] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## the canal server is running now ......
2018-07-23 20:27:47.721 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx1 successful.
2018-07-23 20:27:47.802 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx2 successful.
2018-07-23 20:27:47.862 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx3 successful.
2018-07-23 20:27:47.921 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx4 successful.
2018-07-23 20:27:47.987 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx5 successful.
2018-07-23 20:27:48.044 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx6 successful.
2018-07-23 20:27:48.094 [canal-instance-scan-0] INFO c.a.o.canal.deployer.monitor.SpringInstanceConfigMonitor - auto notify start xxx7 successful.
(2.2) canal正常啟動時(shí)instance對應(yīng)的日志
canal.deployer-1.0.24/logs/example/example.log
2018-07-23 20:27:47.429 [canal-instance-scan-0] INFO c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [canal.properties]
2018-07-23 20:27:47.436 [canal-instance-scan-0] INFO c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [xxx/instance.properties]
2018-07-23 20:27:47.444 [canal-instance-scan-0] WARN org.springframework.beans.TypeConverterDelegate - PropertyEditor [com.sun.beans.editors.EnumEditor] found through deprecated global PropertyEditorManager fallback - consider using a more isolated form of registration, e.g. on the BeanWrapper/BeanFactory!
2018-07-23 20:27:47.451 [canal-instance-scan-0] INFO c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start CannalInstance for 1-xxx
2018-07-23 20:27:47.453 [canal-instance-scan-0] INFO c.a.otter.canal.instance.core.AbstractCanalInstance - start successful....
2018-07-23 20:27:47.666 [destination = xxx , address = /127.0.0.1:3306 , EventParser] WARN c.a.otter.canal.parse.inbound.mysql.MysqlEventParser - prepare to find start position just show master status
(3)停止canal
sh stop.sh` 或 `./bin/stop.sh
2018-07-23 21:45:08.241 [Thread-5] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## stop the canal server
2018-07-23 21:45:08.296 [Thread-5] INFO com.alibaba.otter.canal.deployer.CanalController - ## stop the canal server[10.0.62.130:11111]
2018-07-23 21:45:08.296 [Thread-5] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## canal server is down.
(4) 程序中使用
以下代碼僅作為示例
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.4</version>
</dependency>
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* CanalTest
*
* @author: weikeqin.cn@gmail.com
* @date: 2020-05-30 08:26
**/
@Slf4j
public class CanalTest {
/**
* @param args
*/
public static void main(String args[]) {
String canalHost = "127.0.0.1";
int canalPort = 11111;
String destination = "example";
InetSocketAddress address = new InetSocketAddress(canalHost, canalPort);
// 創(chuàng)建鏈接
CanalConnector connector = CanalConnectors.newSingleConnector(address, destination, "", "");
// connector = CanalConnectors.newClusterConnector(addresses, destination, "", "");
int batchSize = 1000;
int emptyCount = 0;
try {
// 鏈接對應(yīng)的canal server
connector.connect();
// 客戶端訂閱,不提交客戶端filter夕吻,以服務(wù)端的filter為準(zhǔn)
connector.subscribe();
// 回滾到未進(jìn)行ack的地方诲锹,下次fetch的時(shí)候,可以從最后一個(gè)沒有 ack 的地方開始拿
connector.rollback();
int totalEmptyCount = 12000000;
// 退出條件 一般是 while true
while (emptyCount < totalEmptyCount) {
// 獲取指定數(shù)量的數(shù)據(jù)
Message message = connector.getWithoutAck(batchSize);
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
emptyCount++;
log.info("empty count : " + emptyCount);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.info("", e);
}
} else {
emptyCount = 0;
log.info("message[batchId={},size={}] ", batchId, size);
// 消費(fèi)
consumeMsg(message.getEntries());
}
// 提交確認(rèn)
connector.ack(batchId);
// 處理失敗, 回滾數(shù)據(jù)
// connector.rollback(batchId);
}
log.info("empty too many times, exit");
} finally {
// 釋放鏈接
connector.disconnect();
}
}
/**
* 消費(fèi)消息
*
* @param entries
*/
private static void consumeMsg(List<CanalEntry.Entry> entries) {
// 這里只打印
printEntry(entries);
// TODO 其它操作
}
/**
* @param entrys
*/
private static void printEntry(List<CanalEntry.Entry> entrys) {
for (CanalEntry.Entry entry : entrys) {
if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
continue;
}
CanalEntry.RowChange rowChage = null;
try {
rowChage = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
} catch (Exception e) {
throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
}
CanalEntry.EventType eventType = rowChage.getEventType();
log.info(String.format("================ binlog[%s:%s] , name[%s,%s] , eventType : %s",
entry.getHeader().getLogfileName(),
entry.getHeader().getLogfileOffset(),
entry.getHeader().getSchemaName(),
entry.getHeader().getTableName(),
eventType)
);
for (CanalEntry.RowData rowData : rowChage.getRowDatasList()) {
if (eventType == CanalEntry.EventType.DELETE) {
printColumn(rowData.getBeforeColumnsList());
} else if (eventType == CanalEntry.EventType.INSERT) {
printColumn(rowData.getAfterColumnsList());
} else {
log.info("------- before");
printColumn(rowData.getBeforeColumnsList());
log.info("------- after");
printColumn(rowData.getAfterColumnsList());
}
}
}
}
/**
* @param columns
*/
private static void printColumn(List<CanalEntry.Column> columns) {
Map<String, String> map = new HashMap<>();
for (CanalEntry.Column column : columns) {
map.put(column.getName(), column.getValue());
//log.info(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
}
log.info("{}", map);
}
}
(5) canal復(fù)制原理
復(fù)制如何工作涉馅,整體上來說归园,復(fù)制有3個(gè)步驟:
(1) master將改變記錄到二進(jìn)制日志(binary log)中(這些記錄叫做二進(jìn)制日志事件,binary log events)稚矿;
(2) slave將master的binary log events復(fù)制到它的中繼日志(relay log)中庸诱;
(3) slave讀取中繼日志中的事件,將其重放到備庫數(shù)據(jù)之上晤揣。下圖描述了復(fù)制的過程:
[圖片上傳失敗...(image-b777c3-1662962030216)]
(6) 遇到的問題
(6.1) Error When doing Client Authentication:ErrorPacket
Caused by: java.io.IOException: connect /127.0.0.1:3306 failure:java.io.IOException: Error When doing Client Authentication:ErrorPacket [errorNumber=1045, fieldCount=-1, message=Access denied for user 'canal'@'localhost' (using password: YES), sqlState=28000, sqlStateMarker=#]
at com.alibaba.otter.canal.parse.driver.mysql.MysqlConnector.negotiate(MysqlConnector.java:208)
at com.alibaba.otter.canal.parse.driver.mysql.MysqlConnector.connect(MysqlConnector.java:71)
at com.alibaba.otter.canal.parse.inbound.mysql.MysqlConnection.connect(MysqlConnection.java:56)
at com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser.preDump(MysqlEventParser.java:86)
at com.alibaba.otter.canal.parse.inbound.AbstractEventParser$3.run(AbstractEventParser.java:157)
at java.lang.Thread.run(Thread.java:748)
原因 用戶名密碼不正確
(6.2) Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation
ERROR com.alibaba.otter.canal.common.alarm.LogAlarmHandler - destination:xx
[com.alibaba.otter.canal.parse.exception.CanalParseException: command : 'show master status' has an error!
Caused by: java.io.IOException: ErrorPacket [errorNumber=1227, fieldCount=-1, message=Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation, sqlState=42000, sqlStateMarker=#]
with command: show master status
用canal賬戶登錄后發(fā)現(xiàn)可以查看對應(yīng)數(shù)據(jù)庫對應(yīng)表的數(shù)據(jù)桥爽,但是
show master status
提示 Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation1、
instance.properties
配置文件里配置的用戶沒有REPLICATION權(quán)限
2昧识、canal instance.properties 配置錯(cuò)誤
3钠四、配置文件里用戶名密碼不正確
4、MySQL對應(yīng)用戶不存在
5跪楞、MySQL配置不對給canal用戶對應(yīng)的replication權(quán)限
grant replication client on *.* to 'canal'@'%';
flush privileges
(6.3) Access denied; you need (at least one of) the REPLICATION SLAVE privilege(s) for this operation
[destination = xxx , address = /127.0.0.1:3306 , EventParser] ERROR com.alibaba.otter.canal.common.alarm.LogAlarmHandler - destination:xx[java.io.IOException: Received error packet: errno = 1227, sqlstate = 42000 errmsg = Access denied; you need (at least one of) the REPLICATION SLAVE privilege(s) for this operation
at com.alibaba.otter.canal.parse.inbound.mysql.dbsync.DirectLogFetcher.fetch(DirectLogFetcher.java:95)
Access denied 沒權(quán)限 需要給對應(yīng)賬戶授權(quán)
REPLICATION SLAVE 常用于建立復(fù)制時(shí)所需要用到的用戶權(quán)限缀去,也就是slave server必須被master server授權(quán)具有該權(quán)限的用戶,才能通過該用戶復(fù)制甸祭。
并且”SHOW SLAVE HOSTS”這條命令和REPLICATION SLAVE權(quán)限有關(guān)缕碎,否則執(zhí)行時(shí)會報(bào)錯(cuò):REPLICATION CLIENT 不可用于建立復(fù)制,有該權(quán)限時(shí)淋叶,只是多了可以使用如”SHOW SLAVE STATUS”阎曹、”SHOW MASTER STATUS”等命令伪阶。
在5.6.6版本以后,也可以使用”SHOW BINARY LOGS”处嫌。GRANT REPLICATION SLAVE ON *.* TO 'canal'@'%'` `flush privileges
(6.4) canal用了UseConcMarkSweepGC不能用JDK14
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option PermSize; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option MaxPermSize; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option CMSParallelRemarkEnabled; support was removed in 14.0
Unrecognized VM option 'UseCMSCompactAtFullCollection'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
修改 bin/start.sh
文件栅贴,修改對應(yīng)的JAVA路徑
## set java path
if [ -z "$JAVA" ] ; then
#JAVA=$(which java)
JAVA="/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/bin/java"
fi
(6.5) com.alibaba.otter.canal.protocol.exception.CanalClientException: java.net.ConnectException: Connection refused
Exception in thread "main" com.alibaba.otter.canal.protocol.exception.CanalClientException: java.net.ConnectException: Connection refused
at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.doConnect(SimpleCanalConnector.java:198)
at com.alibaba.otter.canal.client.impl.SimpleCanalConnector.connect(SimpleCanalConnector.java:115)
canal沒啟動 或者 canal掛了
配置被刪了,檢查對應(yīng)destination
和instance.properties
instance.properties
沒配置
(7) canal-admin后臺管理
canal-admin設(shè)計(jì)上是為canal提供整體配置管理熏迹、節(jié)點(diǎn)運(yùn)維等面向運(yùn)維的功能檐薯,提供相對友好的WebUI操作界面,方便更多用戶快速和安全的操作
簡單來說注暗,canal-admin是一個(gè)后臺維護(hù)系統(tǒng)坛缕,簡化了配置canal的工作,提高了效率捆昏,終于不用到服務(wù)器上一個(gè)一個(gè)配了
訪問地址 http://127.0.0.1:8089/
(7.1) canal-admin的核心模型主要有
instance赚楚,對應(yīng)canal-server里的instance,一個(gè)最小的訂閱mysql的隊(duì)列
server骗卜,對應(yīng)canal-server宠页,一個(gè)server里可以包含多個(gè)instance
集群,對應(yīng)一組canal-server寇仓,組合在一起面向高可用HA的運(yùn)維
References
[1] canal/wiki
[2] canal-AdminGuide
[3] ClientExample
[4] Canal-Admin-QuickStart
[5] Canal-Admin-Guide
[6] canal配置使用
[7] Mysql 普通賬戶授權(quán)replication client后登錄失敗問題
[8] REPLICATION SLAVE 與 REPLICATION CLIENT 權(quán)限
[9] 對replication slave,replication client的一點(diǎn)說明
[10] MySQL 5.6 Reference Manual – 6.2.1 Privileges Provided by MySQL
[11] SimpleCanalClientTest
[12] ClusterCanalClientTest
2,原理簡介
- canal 模擬 MySQL slave 的交互協(xié)議举户,偽裝自己為 MySQL slave ,向 MySQL master 發(fā)送dump 協(xié)議
- MySQL master 收到 dump 請求遍烦,開始推送 binary log 給 slave (即 canal )
- canal 解析 binary log 對象(原始為 byte 流)
(1)數(shù)據(jù)庫主從同步
- Mysql Master 首先將數(shù)據(jù)寫入二進(jìn)制文件(binary log)俭嘁,可以通過 show binlog events 進(jìn)行查看
- Mysql Slave 將Mysql 的 binary log events 拷貝到中間日志(relay log)
- Mysql Slave 重放 relay log中事件,將數(shù)據(jù)變更反映他自己的數(shù)據(jù)
(2)同步一致性問題
「MySQL的復(fù)制分為:異步復(fù)制服猪、半同步復(fù)制供填、全同步復(fù)制÷Γ」
1),異步復(fù)制
MySQL默認(rèn)的復(fù)制即是異步復(fù)制捕虽,主庫在執(zhí)行完客戶端提交的事務(wù)后會立即將結(jié)果返給給客戶端,并不關(guān)心從庫是否已經(jīng)接收并處理坡脐,這樣就會有一個(gè)問題泄私,「主如果crash掉了,此時(shí)主上已經(jīng)提交的事務(wù)可能并沒有傳到從庫上备闲,如果此時(shí)晌端,強(qiáng)行將從提升為主,可能導(dǎo)致新主上的數(shù)據(jù)不完整恬砂∵志溃」
2),全同步復(fù)制
指當(dāng)主庫執(zhí)行完一個(gè)事務(wù),所有的從庫都執(zhí)行了該事務(wù)才返回給客戶端泻骤。「因?yàn)樾枰却袕膸靾?zhí)行完該事務(wù)才能返回」漆羔,所以全同步復(fù)制的性能必然會收到嚴(yán)重的影響梧奢。
3),半同步復(fù)制
是介于全同步復(fù)制與全異步復(fù)制之間的一種,「主庫只需要等待至少一個(gè)從庫節(jié)點(diǎn)收到」并且 Flush Binlog 到 Relay Log 文件即可演痒,主庫不需要等待所有從庫給主庫反饋亲轨。同時(shí),「這里只是一個(gè)收到的反饋鸟顺,而不是已經(jīng)完全完成并且提交的反饋」惦蚊,如此,節(jié)省了很多時(shí)間讯嫂。
(3)canal.admin蹦锋、adapter和deployer的區(qū)別
canal.example
是demo工程
canal-admin
canal-admin設(shè)計(jì)上是為canal提供整體配置管理、節(jié)點(diǎn)運(yùn)維等面向運(yùn)維的功能欧芽,提供相對友好的WebUI操作界面莉掂,方便更多用戶快速和安全的操作
canal-adapter
增加客戶端數(shù)據(jù)落地的適配及啟動功能(支持Sync HBase、Sync ES 等)
canal 1.1.1版本之后, 內(nèi)置增加客戶端數(shù)據(jù)同步功能,
canal-deployer
這個(gè)就相當(dāng)于canal的服務(wù)端渐裸,啟動它才可以在客戶端接收數(shù)據(jù)庫變更信息巫湘。
3,常見問題排查
(1)表結(jié)構(gòu)元數(shù)據(jù)的存儲問題(配置項(xiàng)里面使用了tsdb
也就是時(shí)序數(shù)據(jù)庫的字眼昏鹃,下面就稱為tsdb
功能)。
-
解決方案1:
environment
- canal 1.1.1
- mysql 5.7
Issue Description
當(dāng)前測試過修改表結(jié)構(gòu)的情況(注意:h2.mv.db的文件所在路徑為:CANAL_HOME/canal.deployer/conf/example/):
1.表添加字段:需要重啟canal诀诊,不重啟同步失效洞渤;刪除 h2.mv.db 后再重啟,同步生效属瓣。
2.表修改字段:不需要重啟载迄,只能同步未修改前的表結(jié)構(gòu)數(shù)據(jù)(返回的還是未修改字段名的字段);刪除 h2.mv.db 后再重啟抡蛙,修改的字段生效护昧。
3.刪除字段不需要重啟,也不需要刪除 h2.mv.db粗截,即時(shí)生效惋耙。 -
解決方案2:
默認(rèn)開啟
tsdb
功能,也就是會通過h2
數(shù)據(jù)庫緩存解析的表結(jié)構(gòu),但是實(shí)際情況下,如果上游變更了表結(jié)構(gòu)至非,h2
數(shù)據(jù)庫對應(yīng)的緩存是不會更新的习劫,這個(gè)時(shí)候一般會出現(xiàn)神奇的解析異常届腐,異常的信息一般如下:Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: column size is not match for table:數(shù)據(jù)庫名稱.表名稱,新表結(jié)構(gòu)的字段數(shù)量 vs 緩存表結(jié)構(gòu)的字段數(shù)量;
該異常還會導(dǎo)致一個(gè)可怕的后果:解析線程被阻塞傀顾,也就是
binlog
事件不會再接收和解析嫉拐。這個(gè)問題筆者也查看過很多Issue
漠嵌,大家都認(rèn)為是一個(gè)嚴(yán)重的BUG
,目前認(rèn)為比較可行的解決方案是:禁用tsdb
功能(真的夠粗暴),也就是canal.instance.tsdb.enable
設(shè)置為false
。如果不禁用tsdb
功能,一旦出現(xiàn)了該問題报腔,必須要先停止Canal
服務(wù)翻诉,接著刪除$CANAL_HOME/conf/目標(biāo)數(shù)據(jù)庫實(shí)例標(biāo)識/h2.mv.db
文件绅作,然后啟動Canal
服務(wù)蛾派。因?yàn)檫@個(gè)比較坑的問題,筆者在生產(chǎn)禁用了
tsdb
功能眯杏,并且添加了DDL
語句的處理邏輯,直接打到釘釘預(yù)警上并且@
整個(gè)群的人岂贩。
(2)Mysql的數(shù)據(jù)binlog的格式必須是Row模式
Binlog解析錯(cuò)誤:重復(fù)解析/DML解析為QUERY
- INSERT/UPDATE/DELETE被解析為Query或DDL語句
- Binlog重復(fù)解析,即一個(gè)操作又有QUERY消息垮耳,又有對應(yīng)的INSERT/UPDATE/DELETE消息。
1)妓湘,Row模式
Binlog日志中僅記錄哪一條記錄被修改了查蓉,修改成什么樣了,會非常清楚的記錄下每一行數(shù)據(jù)修改的細(xì)節(jié)榜贴,「Master修改了哪些行豌研,slave也直接修改對應(yīng)行的數(shù)據(jù)」
問1:INSERT/UPDATE/DELETE被解析為Query或DDL語句?
答1:出現(xiàn)這類情況主要原因?yàn)槭盏降腷inlog就為Query事件唬党,比如:
- binlog格式為非row模式鹃共,通過show variables like 'binlog_format'可以查看. 針對statement/mixed模式,DML語句都會是以SQL語句存在
- mysql5.6+之后驶拱,在binlog為row模式下霜浴,針對DML語句通過一個(gè)開關(guān)(binlog-rows-query-log-events=true, show variables里也可以看到該變量),記錄DML的原始SQL蓝纲,對應(yīng)binlog事件為RowsQueryLogEvent阴孟,同時(shí)也有對應(yīng)的row記錄. ps. canal可以通過properties設(shè)置來過濾:canal.instance.filter.query.dml = true
2),Statement模式
每一條會修改數(shù)據(jù)的sql都會記錄到master的binlog中晌纫,「slave在復(fù)制的時(shí)候sql進(jìn)程會解析成和原來master端執(zhí)行相同的sql再執(zhí)行∮浪浚」
3),Mix模式
「在 Mixed 模式下锹漱,MySQL 會根據(jù)執(zhí)行的每一條具體的 SQL 語句來區(qū)分對待記錄的日志形式,也就是在 statement 和 row 之間選擇一種慕嚷「珉梗」
(3)Filter失效
配置方式有兩種,一種是使用配置文件的方式喝检;另一種是使用代碼的方式指定嗅辣;
使用代碼的方式優(yōu)先級要大于配置的方式;那么此時(shí)就會出現(xiàn)導(dǎo)致Filter失效蛇耀。
-
配置文件方式
我們通常在canal-server端的conf/example/instance.properties文件中進(jìn)行設(shè)置
mysql 數(shù)據(jù)解析關(guān)注的表辩诞,Perl正則表達(dá)式.
多個(gè)正則之間以逗號(,)分隔,轉(zhuǎn)義符需要雙斜杠(\\)
常見例子:
1. 所有表:.* or .*\\..*
2. canal schema下所有表:canal\\..*
3. canal下的以canal打頭的表:canal\\.canal.*
4. canal schema下的一張表:canal.test1
5. 多個(gè)規(guī)則組合使用:canal\\..*,mysql.test1,mysql.test2 (逗號分隔)
-
使用代碼的方式
也可以在客戶端與canal進(jìn)行連接時(shí)纺涤,用客戶端的
connector.subscribe("xxxxxxx");
來覆蓋服務(wù)端初始化時(shí)的設(shè)置译暂。
(4)消費(fèi)落后
部署完Canal后,在遇到數(shù)據(jù)庫寫入高峰期撩炊,就遇到了數(shù)據(jù)延遲問題外永。數(shù)據(jù)延遲還是小事,但是一旦延遲到堆滿了內(nèi)存緩沖區(qū)拧咳,不消費(fèi)的話伯顶,新的消息就進(jìn)不來了。
具體的設(shè)計(jì)可以參考這個(gè)網(wǎng)址
解決方案:
「一個(gè)可行的解決辦法是骆膝,將消息拉取后祭衩,寫入消息隊(duì)列(如RabbitMQ/Kafka),用消息隊(duì)列來堆積消息處理阅签,來保證大量消息堆積后不會導(dǎo)致canal卡死掐暮,并且可以支持?jǐn)?shù)據(jù)持久化≌樱」
(5),未配置MySQL-master連接信息
- 報(bào)錯(cuò)信息
2021-12-12 20:11:04.251 [destination = example , address = null , EventParser] ERROR c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - parse events has an error
com.alibaba.otter.canal.parse.exception.CanalParseException: illegal connection is null
-
修改配置文件信息路克,追加master的配置
canal.instance.master.address = 127.0.0.1:3306
校驗(yàn)meta,dat 元數(shù)據(jù)信息
[yunwei@host-192-124-16-73 example]# ls
h2.mv.db h2.trace.db instance.properties meta.dat
[yunwei@host-192-124-16-73 example]# pwd
/app/canal.deployer/conf/example
[yunwei@host-192-124-16-73 example]# cat meta.dat
{"clientDatas":[{"clientIdentity":{"clientId":1001,"destination":"example","filter":""},"cursor":{"identity":{"slaveId":-1,"sourceAddress":{"address":"192.124.16.90","port":3306}},"postion":{"gtid":"","included":false,"journalName":"mysql-bin.000008","position":4918,"serverId":1,"timestamp":1639312041000}}}],"destination":"example"}
- 在數(shù)據(jù)庫中進(jìn)行對比查看
-- 查看binlog的日志存儲
mysql> show binary logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 177 |
| mysql-bin.000002 | 1131 |
| mysql-bin.000003 | 792 |
| mysql-bin.000004 | 45420039 |
| mysql-bin.000005 | 2943 |
| mysql-bin.000006 | 862 |
| mysql-bin.000007 | 9082 |
| mysql-bin.000008 | 4949 |
+------------------+-----------+
8 rows in set (0.00 sec)
-- 查看當(dāng)前已經(jīng)消費(fèi)到了什么位置
mysql> show binlog events in 'mysql-bin.000008';
+------------------+------+----------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+----------------+-----------+-------------+---------------------------------------+
| mysql-bin.000008 | 4 | Format_desc | 1 | 123 | Server ver: 5.7.27-log, Binlog ver: 4 |
| mysql-bin.000008 | 123 | Previous_gtids | 1 | 154 | |
| mysql-bin.000008 | 154 | Anonymous_Gtid | 1 | 219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 219 | Query | 1 | 291 | BEGIN |
| mysql-bin.000008 | 291 | Table_map | 1 | 402 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 402 | Write_rows | 1 | 808 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 808 | Xid | 1 | 839 | COMMIT /* xid=108 */ |
| mysql-bin.000008 | 839 | Anonymous_Gtid | 1 | 904 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 904 | Query | 1 | 976 | BEGIN |
| mysql-bin.000008 | 976 | Table_map | 1 | 1087 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 1087 | Write_rows | 1 | 1493 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 1493 | Xid | 1 | 1524 | COMMIT /* xid=115 */ |
| mysql-bin.000008 | 1524 | Anonymous_Gtid | 1 | 1589 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 1589 | Query | 1 | 1661 | BEGIN |
| mysql-bin.000008 | 1661 | Table_map | 1 | 1772 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 1772 | Write_rows | 1 | 2178 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 2178 | Xid | 1 | 2209 | COMMIT /* xid=123 */ |
| mysql-bin.000008 | 2209 | Anonymous_Gtid | 1 | 2274 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 2274 | Query | 1 | 2346 | BEGIN |
| mysql-bin.000008 | 2346 | Table_map | 1 | 2457 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 2457 | Write_rows | 1 | 2863 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 2863 | Xid | 1 | 2894 | COMMIT /* xid=158 */ |
| mysql-bin.000008 | 2894 | Anonymous_Gtid | 1 | 2959 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 2959 | Query | 1 | 3031 | BEGIN |
| mysql-bin.000008 | 3031 | Table_map | 1 | 3142 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 3142 | Write_rows | 1 | 3548 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 3548 | Xid | 1 | 3579 | COMMIT /* xid=192 */ |
| mysql-bin.000008 | 3579 | Anonymous_Gtid | 1 | 3644 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 3644 | Query | 1 | 3716 | BEGIN |
| mysql-bin.000008 | 3716 | Table_map | 1 | 3827 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 3827 | Write_rows | 1 | 4233 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 4233 | Xid | 1 | 4264 | COMMIT /* xid=215 */ |
| mysql-bin.000008 | 4264 | Anonymous_Gtid | 1 | 4329 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000008 | 4329 | Query | 1 | 4401 | BEGIN |
| mysql-bin.000008 | 4401 | Table_map | 1 | 4512 | table_id: 110 (zhsq.t_vehicle_record) |
| mysql-bin.000008 | 4512 | Write_rows | 1 | 4918 | table_id: 110 flags: STMT_END_F |
| mysql-bin.000008 | 4918 | Xid | 1 | 4949 | COMMIT /* xid=234 */ |
+------------------+------+----------------+-----------+-------------+---------------------------------------+
37 rows in set (0.00 sec)
參考連接
2精算,【開源實(shí)戰(zhàn)】Canal部署常見問題:重復(fù)解析/Filter失效/消費(fèi)落后
4碎连,修改表結(jié)構(gòu)之后是否重啟Canal
5灰羽,Canal筆記