MySQL Binlog 增量同步工具 go-mysql-transfer 實現詳解
一、 概述
工作需要研究了下阿里開源的 MySQL Binlog 增量訂閱消費元件 canal,其功能強大、執行穩定,但是有些方面不是太符合需求,主要有如下三點:
1、需要自己編寫客戶端來消費 canal 解析到的資料
2、server-client 模式,需要同時部署 server 和 client 兩個元件,我們的專案中有 6 個業務資料庫要實時同步到 redis,意味著要多部署 12 個元件,硬體和運維成本都會增加。
3、從 server 端到 client 端需要經過一次網路傳輸和序列化反序列化操作,然後再同步到接收端,感覺沒有直接懟到接收端更高效。
go-mysql-transfer 是使用 Go 語言實現的 MySQL 資料庫實時增量同步工具, 參考 Canal 但是規避了上述三點。旨在實現一個高效能、低延遲、簡潔易用的 Binlog 增量資料同步管道, 具有如下特點:
1、不依賴其它元件,一鍵部署
2、整合多種接收端,如:Redis、MongoDB、Elasticsearch、RocketMQ、Kafka、RabbitMQ,不需要再編寫客戶端,開箱即用
3、內建豐富的資料解析、訊息生成規則;支援 Lua 指令碼,以處理更復雜的資料邏輯
4、支援監控告警,整合 Prometheus 客戶端
5、高可用叢集部署
6、資料同步失敗重試
7、全量資料初始化
二、 與同類工具比較
特色 | Canal | mysql_stream | go-mysql-transfer |
---|---|---|---|
開發語言 | Java | Python | Golang |
HA | 支援 | 支援 | 支援 |
接收端 | 編碼定製 | Kafka 等 | Redis、MongoDB、Elasticsearch、 RabbitMQ、Kafka、RocketMQ 後續支援更多 |
資料初始化 | 不支援 | 支援 | 支援 |
資料格式 | 編碼定製 | json(固定) | 規則(固定) Lua 指令碼 (定製) |
三、 設計實現
1、實現原理
go-mysql-transfer 將自己偽裝成 MySQL 的 Slave,向 Master 傳送 dump 協議獲取 binlog,解析 binlog 並生成訊息,實時傳送給接收端。
2、資料轉換規則
將從 binlog 解析出來的資料,經過簡單的處理轉換髮送到接收端。使用內建豐富數資料轉換規則,可完成大部分同步工作。
例如將表 t_user 同步到 reids,配置如下規則:
rule:
-
schema: eseap #資料庫名稱
table: t_user #表名稱
column_underscore_to_camel: true #列名稱下劃線轉駝峰,預設為false
datetime_formatter: yyyy-MM-dd HH:mm:ss #datetime、timestamp型別格式化,不填寫預設yyyy-MM-dd HH:mm:ss
value_encoder: json #值編碼型別,支援json、kv-commas、v-commas
redis_structure: string # redis資料型別。 支援string、hash、list、set型別(與redis的資料型別一致)
redis_key_prefix: USER_ #key字首
redis_key_column: USER_NAME #使用哪個列的值作為key,不填寫預設使用主鍵
t_user 表,資料如下:
同步到 Redis 後,資料如下:
更多規則配置和同步案例 請見後續的"使用說明"章節。
3、資料轉換指令碼
Lua 是一種輕量小巧的指令碼語言, 其設計目的是為了嵌入應用程式中,從而為應用程式提供靈活的擴充套件和定製功能。開發者只需要花費少量時間就能大致掌握 Lua 的語法,照虎畫貓寫出可用的指令碼。
基於 Lua 的高擴充套件性,可以實現更為複雜的資料解析、訊息生成邏輯,定製需要的資料格式。
使用方式:
rule:
-
schema: eseap
table: t_user
lua_file_path: lua/t_user_string.lua #lua指令碼檔案
示例指令碼:
local json = require("json") -- 載入json模組
local ops = require("redisOps") -- 載入redis操作模組
local row = ops.rawRow() --當前變動的一行資料,table型別,key為列名稱
local action = ops.rawAction() --當前資料庫的操作事件,包括:insert、updare、delete
local id = row["ID"] --獲取ID列的值
local userName = row["USER_NAME"] --獲取USER_NAME列的值
local key = "user_"..id -- 定義key
if action == "delete" -- 刪除事件
then
ops.DEL(key) -- 刪除KEY
else
local password = row["PASSWORD"] --獲取USER_NAME列的值
local createTime = row["CREATE_TIME"] --獲取CREATE_TIME列的值
local result= {} -- 定義結果
result["id"] = id
result["userName"] = userName
result["password"] = password
result["createTime"] = createTime
result["source"] = "binlog" -- 資料來源
local val = json.encode(result) -- 將result轉為json
ops.SET(key,val) -- 對應Redis的SET命令,第一個引數為key(string型別),第二個引數為value
end
t_user 表,資料如下:
同步到 Redis 後,資料如下:
更多 Lua 指令碼使用說明 和同步案例 請見後續的"使用說明"章節。
4、監控告警
Prometheus 是流行開源監控報警系統和 TSDB,其指標採集元件被稱作 exporter。go-mysql-transfer 本身就是一個 exporter。向 Prometheus 提供應用狀態、接收端狀態、insert 數量、update 數量、delete 數量、delay 延時等指標。
go-mysql-transfer 內建 Prometheus exporter 可以監控系統的執行狀況,並進行健康告警。
相關配置:
enable_exporter: true #啟用prometheus exporter,預設false
exporter_addr: 9595 #prometheus exporter埠,預設9595
直接訪問 127.0.0.1:9595 可以看到匯出的指標值,如何與 Prometheus 整合,請參見 Prometheus 相關教程。
指標說明:
transfer_leader_state:當前節點是否為 leader,0=否、1=是
transfer_destination_state:接收端狀態, 0=掉線、1=正常
transfer_inserted_num:插入資料的數量
transfer_updated_num:修改資料的數量
transfer_deleted_num:刪除資料的數量
transfer_delay:與 MySQL Master 的時延
5、高可用
可以選擇依賴 zookeeper 或者 etcdr 構建高可用叢集,一個叢集中只存在一個 leader 節點,其餘皆為 follower 節點。
只有 leader 節點響應 binglog 的 dump 事件,follower 節點為蟄伏狀態,不傳送 dump 命令,因此多個 follower 也不會加重 Master 的負擔。
當 leader 節點出現故障,follower 節點迅速替補上去,實現秒級故障切換。
相關配置:
cluster: # 叢集配置
name: myTransfer #叢集名稱,具有相同name的節點放入同一個叢集
# ZooKeeper地址,多個用逗號分隔
zk_addrs: 192.168.1.10:2181,192.168.1.11:2182,192.168.1.12:2183
#zk_authentication: 123456 #digest型別的訪問祕鑰,如:user:password,預設為空
#etcd_addrs: 192.168.1.10:2379 #etcd連線地址,多個用逗號分隔
#etcd_user: test #etcd使用者名稱
#etcd_password: 123456 #etcd密碼
6、失敗重試
網路抖動、接收方故障都會導致資料同步失敗,需要有重試機制,才能保證不漏掉資料,使得每一條資料都能送達。
通常有兩種重試實現方式,一種方式是記錄下故障時刻 binglog 的 position(位移),等故障恢復後,從 position 處重新 dump 資料,傳送給接收端。
一種方式是將同步失敗的資料在本地落盤,形成佇列。當探測到接收端可用時,逐條預出列嘗試傳送,傳送成功最終出列。確保不丟資料,佇列先進先出的特性也可保證資料順序性,正確性。
go-mysql-transfer 採用的是後者,目的是減少傳送 dump 命令的次數,減輕 Master 的負擔。因為 binglog 記錄的整個 Master 資料庫的日誌,其增長速度很快。如果只需要拿幾條資料,而 dump 很多資料,有點得不償失。
7、全量資料初始化
如果資料庫原本存在無法通過 binlog 進行增量同步的資料,可以使用命令列工具-stock 完成始化同步。
stock 基於 SELECT * FROM {table}的方式分批查詢出資料,根據規則或者 Lua 指令碼生成指定格式的訊息,批量傳送到接收端。
執行命令 go-mysql-transfer -stoc,在控制檯可以直觀的看到資料同步狀態,如下:
四、安裝
二進位制安裝包
直接下載編譯好的安裝包: 點選下載
原始碼編譯
1、依賴 Golang 1.14 及以上版本
2、設定' GO111MODULE=on '
3、拉取原始碼 ‘ go get -d github.com/wj596/go-mysql-transfer’
3、進入目錄,執行 ‘ go build ’ 編譯
五、部署執行
開啟 MySQL 的 binlog
#Linux在my.cnf檔案
#Windows在my.ini檔案
log-bin=mysql-bin # 開啟 binlog
binlog-format=ROW # 選擇 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定義,不要和 go-mysql-transfer 的 slave_id 重複
命令列執行
1、修改 app.yml
2、Windows 直接執行 go-mysql-transfer.exe
3、Linux 執行 nohup go-mysql-transfer &
docker 執行
1、拉取原始碼 ‘ go get -d github.com/wj596/go-mysql-transfer’
2、修改配置檔案 ‘ app.yml ’ 中相關配置
3、構建映象 ‘ docker image build -t go-mysql-transfer -f Dockerfile . ’
4、執行 ‘ docker run -d --name go-mysql-transfer -p 9595:9595 go-mysql-transfer:latest ’
六、使用說明
七、開源
github:go-mysql-transfer
八、效能測試
1、測試環境
平臺:虛擬機器 CPU:E7-4890 4 核 8 執行緒 記憶體:8G 硬碟:機械硬碟 OS:Windows Sever 2012 R2 MySQL: 5.5 Rides: 4.0.2
2、測試資料 t_user 表,14 個欄位,1 個欄位包含中文,資料量 527206 條
3、測試配置
規則:
schema: eseap
table: t_user
order_by_column: id #排序欄位,全量資料初始化時不能為空
#column_lower_case:false #列名稱轉為小寫,預設為false
#column_upper_case:false#列名稱轉為大寫,預設為false
column_underscore_to_camel: true #列名稱下劃線轉駝峰,預設為false
# 包含的列,多值逗號分隔,如:id,name,age,area_id 為空時表示包含全部列
#include_column: ID,USER_NAME,PASSWORD
date_formatter: yyyy-MM-dd #date型別格式化, 不填寫預設yyyy-MM-dd
datetime_formatter: yyyy-MM-dd HH:mm:ss #datetime、timestamp型別格式化,不填寫預設yyyy-MM-dd HH:mm:ss
value_encoder: json #值編碼,支援json、kv-commas、v-commas
redis_structure: string # 資料型別。 支援string、hash、list、set型別(與redis的資料型別一直)
redis_key_prefix: USER_ #key的字首
redis_key_column: ID #使用哪個列的值作為key,不填寫預設使用主鍵
指令碼:
local json = require("json") -- 載入json模組
local ops = require("redisOps") -- 載入redis操作模組
local row = ops.rawRow() --當前變動的一行資料,table型別,key為列名稱
local action = ops.rawAction() --當前資料庫的操作事件,包括:insert、updare、delete
local id = row["ID"] --獲取ID列的值
local userName = row["USER_NAME"] --獲取USER_NAME列的值
local key = "user_"..id -- 定義key
if action == "delete" -- 刪除事件
then
ops.DEL(key) -- 刪除KEY
else
local password = row["PASSWORD"] --獲取USER_NAME列的值
local createTime = row["CREATE_TIME"] --獲取CREATE_TIME列的值
local result= {} -- 定義結果
result["id"] = id
result["userName"] = userName
result["password"] = password
result["createTime"] = createTime
result["source"] = "binlog" -- 資料來源
local val = json.encode(result) -- 將result轉為json
ops.SET(key,val) -- 對應Redis的SET命令,第一個引數為key(string型別),第二個引數為value
end
3、測試用例一
使用規則,將 52 萬條資料全量初始化同步到 Redis,結果如下:
3 次執行的中間值為 4.6 秒
4、測試用例二
使用 Lua 指令碼,將 52 萬條資料全量初始化同步到 Redis,結果如下:
3 次執行的中間值為 9.5 秒
5、測試用例三
使用規則,將 binlog 中 52 萬條增量資料同步到 Redis。結果如下:
每秒增量同步 (TPS) 32950 條
6、測試用例四
使用 Lua 指令碼,將 binlog 中 52 萬條增量資料同步到 Redis。結果如下:
每秒增量同步 (TPS) 15819 條
7、測試用例五
100 個執行緒不停向 MySQL 寫資料,使用規則將資料實時增量同步到 Redis,TPS 保持在 4000 以上,資源佔用情況如下:
100 個執行緒不停向 MySQL 寫資料,使用 Lua 指令碼將資料實時增量同步到 Redis,TPS 保持在 2000 以上,資源佔用情況如下:
以上測試結果,會隨著測試環境的不同而改變,僅作為參考。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- MySQL Binlog 解析工具 Maxwell 詳解MySql
- kettle 實現mysql單表增量同步MySql
- Canal:同步mysql增量資料工具,一篇詳解核心知識點MySql
- mysql檢視binlog日誌詳解MySql
- 資料庫系列——基於Canal實現MySQL增量資料同步資料庫MySql
- linux 利用rsync實現檔案增量同步Linux
- 基於 MySQL Binlog 的 Elasticsearch 資料同步實踐MySqlElasticsearch
- 用canal監控binlog並實現mysql定製同步資料的功能MySql
- Kafka Connect如何實現同步RDS binlog資料?Kafka
- 阿里 Canal 實時同步 MySQL 增量資料至 ClickHouse 資料庫阿里MySql資料庫
- mysql閃回工具binlog2sqlMySql
- 教你MySQL Binlog實用攻略MySql
- RestCloud ETL實踐之無標識位實現增量資料同步RESTCloud
- Linux 遠端資料同步工具詳解Linux
- 什麼是非同步?5大非同步程式設計實現詳解!非同步程式設計
- etl 增量對比解決方案 etl-engine 如何實現增量對比
- mysql point in time recovery using sql_thread SQL_Thread增量恢復binlog 要點MySqlthread
- 使用canal增量同步mysql資料庫資訊到ElasticSearchMySql資料庫Elasticsearch
- MySQL工具之binlog2sql閃回操作MySql
- 監聽MySQL的binlog日誌工具分析:CanalMySql
- 詳解MYSQL的備份還原(PHP實現)MySqlPHP
- Restcloud ETl資料通過時間戳實現增量資料同步RESTCloud時間戳
- MySQL:Redo & binlogMySql
- flnkcdc+datastream實現mysql到mysql資料同步ASTMySql
- kettle從入門到精通 第六十九課 ETL之kettle kettle cdc mysql,輕鬆實現實時增量同步MySql
- 實戰!Spring Boot 整合 阿里開源中介軟體 Canal 實現資料增量同步!Spring Boot阿里
- Flink增量快照的實現
- MySQL系列:binlog日誌詳解(引數、操作、GTID、最佳化、故障演練)MySql
- 揭秘MySQL的主從同步實現方案MySql主從同步
- DRBD物理同步方式實現MySQL之高可用MySql
- MySQL怎麼實現主從同步和Django實現MySQL讀寫分離MySql主從同步Django
- Mysql的binlog原理MySql
- MySQL Binlog 介紹MySql
- 實戰-MySQL定時增量備份(2)MySql
- 東商專案mysql例項庫(dingding)增量備份的實現MySql
- CanalSharp-mysql資料庫binlog的增量訂閱&消費元件Canal的.NET客戶端MySql資料庫元件客戶端
- 【Maxwell】使用maxwell+kafka+python做binlog增量解析消費KafkaPython
- mysql和redis實時同步資料怎麼實現MySqlRedis