前提
在忍耐了很久之后,忍不住爆發了,在掘金發了條沸點(下班時發的):
這是一個令人悲傷的故事,這條情感爆發的沸點好像被屏蔽了,另外小水渠(Canal
意為水道、管道)上線一段時間,不出坑的時候風平浪靜,一旦出坑令人想屎。重點吐槽幾點:
- 目前最新的
RELEASE
版本為v1.1.4
,發布於2019-9-2
,快一年沒更新了。 Issue
里面堆積了十分多未處理或者沒有回應的問題,有不少問題的年紀比較大。master
分支經常提交異常的代碼,構建不友好,因為v1.1.4
比較多問題,也曾經想過用master
代碼手動構建,導入項目之后決定放棄,誰試試誰知道,可以嘗試對比導入和構建MyBatis
的源碼。
這些都只是表象,下面聊聊踩過的坑。
解析線程阻塞問題
這個基本是每個使用Canal
的開發者的必踩之坑。$CANAL_HOME/conf/canal.properties
配置文件中存在一行注釋掉的配置:canal.instance.parser.parallelThreadSize = 16
。該配置用於指定解析器實例並發線程數,如果注釋了會導致解析線程阻塞,得到的結果就是什么都不會發生。
注釋解除即可,建議使用默認值16
。
表結構緩存異常阻塞問題
這是Issue
里面很大部分提問者提到但是久未解決的問題,也就是表結構元數據的存儲問題(配置項里面使用了tsdb
也就是時序數據庫的字眼,下面就稱為tsdb
功能)。
默認開啟tsdb
功能,也就是會通過h2
數據庫緩存解析的表結構,但是實際情況下,如果上游變更了表結構,h2
數據庫對應的緩存是不會更新的,這個時候一般會出現神奇的解析異常,異常的信息一般如下:
Caused by: com.alibaba.otter.canal.parse.exception.CanalParseException: column size is not match for table:數據庫名稱.表名稱,新表結構的字段數量 vs 緩存表結構的字段數量;
該異常還會導致一個可怕的后果:解析線程被阻塞,也就是binlog
事件不會再接收和解析。這個問題筆者也查看過很多Issue
,大家都認為是一個嚴重的BUG
,目前認為比較可行的解決方案是:禁用tsdb
功能(真的夠粗暴),也就是canal.instance.tsdb.enable
設置為false
。如果不禁用tsdb
功能,一旦出現了該問題,必須要先停止Canal
服務,接着刪除$CANAL_HOME/conf/目標數據庫實例標識/h2.mv.db
文件,然后啟動Canal
服務。
因為這個比較坑的問題,筆者在生產禁用了tsdb
功能,並且添加了DDL
語句的處理邏輯,直接打到釘釘預警上並且@
整個群的人。
每次看到這個預警都心驚膽戰。
日志問題
如果剛好需要定位的binlog
位點處於比較靠后的文件,文件數量比較多,會瘋狂打印尋位的日志。之前嘗試過重啟一下子打印了幾GB
日志,超過99%
是定位binlog
文件和position
的日志行。可以考慮通過修改$CANAL_HOME/conf/logback.xml
(並不建議,不清楚源碼容易造成其他新的問題)配置或者指定$CANAL_HOME/conf/目標數據庫實例標識/instance.properties
的下面幾個屬性手動定位解析的起點:
canal.instance.master.journal.name=binlog的文件名
canal.instance.master.position=binlog的文件中的位點
canal.instance.master.timestamp=時間戳
canal.instance.master.gtid=gtid的值
以上的手動定位解析的起點的屬性需要在下次重啟Canal之前更新或者注釋掉,否則會造成重新解析或者找不到文件的嚴重后果!!!
反正每次重啟Canal
服務都驚心動魄,沒有一個開源軟件可以讓人有這種感覺。因為生產的服務器磁盤不是很充足,選配的時候只買了100GB
,而且考慮到這些日志本質上沒有太大意義,於是只能定期上去刪日志,前期是手動刪,后來覺得麻煩寫了個Shell
腳本定時刪除久遠的日志文件。
雲RDS MySQL的使用問題
如果剛好使用了阿里雲的RDS MySQL
,那么有可能會遭遇更大的坑。主要問題是:
RDS MySQL
有磁盤空間優化規則,觸發了規則會把binlog
文件上傳到OSS
,然后刪除本地的binlog
文件。- 從
Canal
的文檔來看,會自動拉取OSS
上的binlog
文件進行解析,讓使用者無感知,但是此功能有BUG
,一直無法正常使用。 RDS MySQL
是一個暗箱,出了問題只能通過MySQL
的相關查詢去定位問題,沒有辦法進去服務器查看真實的現場。
命中了這個問題,一般出現的異常是:
.................. sqlstate = HY000 errmsg = Could not find first log file name in binary log index file
可以基本確認這個功能是存在缺陷的,例如這里有個Issue-2596:
目前筆者的做法如下:
- 完全棄用
Canal
拉取OSS
上的binlog
文件的功能。 RDS MySQL
盡可能擴容一下磁盤,調整策略讓盡可能多的binlog
文件盡可能久地保留在本地,讓它們被完全解析后再手動上傳或者命中了過期規則后自動上傳,這期間有很多東西需要額外收取費用,具體需要自行權衡。
讀取和解析OSS上的binlog文件在目前(2020-08-05)的master分支上依然有BUG,想手動構建master分支的伙伴建議放棄幻想。
這個問題的嚴重后果是:有比較大的可能性導致某段binlog
文件解析完全缺失,除非可以把binlog
文件重新塞回去RDS MySQL
里面,否則需要做上下游手動同步功能。
to be continue
除此之外,要注意Canal
最好做主備部署,提交位點和集群管理建議使用Zookeeper
,而服務模式(canal.serverMode
,目前支持tcp
、kafka
和rocketmq
)建議選用Kafka
(master
分支上有RabbitMQ
的連接器支持,如果想嘗鮮可以手動構建一下),並且每個節點的資源要求比較高,筆者生產上每個節點使用了2C8G
低主頻的ECS
,感覺有點壓不住,特別時重啟實例的時候如果需要重新定位binlog
位點,CPU
在一段時間內使用率會飆高。
筆者發現了阿里雲的DTS
就是使用了Canal
作為基礎中間件進行數據同步的,說明它有被投產到實際應用場景中,真不希望它最終演變成廢棄的KPI
任務項目。不知道往后還會遇到多少問題,如果碰到了也會持續更新本避坑指南。
(本文完 c-2-d e-a-20200805)
這是公眾號《Throwable》發布的原創文章,收錄於專輯《架構與實戰》。