MySQL Binlog增量同步工具go-mysql-transfer实现详解
go-mysql-transfer產品手冊:https://www.kancloud.cn/wj596/go-mysql-transfer/2111996
一、 概述
工作需要研究了下阿里開源的MySQL Binlog增量訂閱消費組件canal,其功能強大、運行穩定,但是有些方面不是太符合需求,主要有如下三點:
go-mysql-transfer是使用Go語言實現的MySQL數據庫實時增量同步工具, 參考Canal但是規避了上述三點。旨在實現一個高性能、低延遲、簡潔易用的Binlog增量數據同步管道, 具有如下特點:
二、 與同類工具比較
三、 設計實現
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 #列名稱下劃線轉駝峰,默認為falsedatetime_formatter: yyyy-MM-dd HH:mm:ss #datetime、timestamp類型格式化,不填寫默認yyyy-MM-dd HH:mm:ssvalue_encoder: json #值編碼類型,支持json、kv-commas、v-commasredis_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: eseaptable: t_userlua_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、deletelocal id = row["ID"] --獲取ID列的值 local userName = row["USER_NAME"] --獲取USER_NAME列的值 local key = "user_"..id -- 定義keyif action == "delete" -- 刪除事件 thenops.DEL(key) -- 刪除KEY else local password = row["PASSWORD"] --獲取USER_NAME列的值local createTime = row["CREATE_TIME"] --獲取CREATE_TIME列的值local result= {} -- 定義結果result["id"] = idresult["userName"] = userNameresult["password"] = passwordresult["createTime"] = createTimeresult["source"] = "binlog" -- 數據來源local val = json.encode(result) -- 將result轉為jsonops.SET(key,val) -- 對應Redis的SET命令,第一個參數為key(string類型),第二個參數為value endt_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,在控制臺可以直觀的看到數據同步狀態,如下:
四、安裝
二進制安裝包
直接下載編譯好的安裝包: https://github.com/wj596/go-mysql-transfer/releases
源碼編譯
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
六、性能測試
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: eseaptable: t_userorder_by_column: id #排序字段,全量數據初始化時不能為空#column_lower_case:false #列名稱轉為小寫,默認為false#column_upper_case:false#列名稱轉為大寫,默認為falsecolumn_underscore_to_camel: true #列名稱下劃線轉駝峰,默認為false# 包含的列,多值逗號分隔,如:id,name,age,area_id 為空時表示包含全部列#include_column: ID,USER_NAME,PASSWORDdate_formatter: yyyy-MM-dd #date類型格式化, 不填寫默認yyyy-MM-dddatetime_formatter: yyyy-MM-dd HH:mm:ss #datetime、timestamp類型格式化,不填寫默認yyyy-MM-dd HH:mm:ssvalue_encoder: json #值編碼,支持json、kv-commas、v-commasredis_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、deletelocal id = row["ID"] --獲取ID列的值 local userName = row["USER_NAME"] --獲取USER_NAME列的值 local key = "user_"..id -- 定義keyif action == "delete" -- 刪除事件 thenops.DEL(key) -- 刪除KEY else local password = row["PASSWORD"] --獲取USER_NAME列的值local createTime = row["CREATE_TIME"] --獲取CREATE_TIME列的值local result= {} -- 定義結果result["id"] = idresult["userName"] = userNameresult["password"] = passwordresult["createTime"] = createTimeresult["source"] = "binlog" -- 數據來源local val = json.encode(result) -- 將result轉為jsonops.SET(key,val) -- 對應Redis的SET命令,第一個參數為key(string類型),第二個參數為value end3、測試用例一
使用規則,將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以上,資源占用情況如下:
以上測試結果,會隨著測試環境的不同而改變,僅作為參考。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的MySQL Binlog增量同步工具go-mysql-transfer实现详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 系统调用和库函数的区别
- 下一篇: cuda nsight 调试和性能分析