mysql binlog 大数据_后起之秀 | MySQL Binlog增量同步工具go-mysql-transfer实现详解

一、 概述

工作需要研究了下阿里开源的MySQL Binlog增量订阅消费组件canal,其功能强大、运行稳定,但是有些方面不是太符合需求,主要有如下三点:

需要自己编写客户端来消费canal解析到的数据

server-client模式,需要同时部署server和client两个组件,我们的项目中有6个业务数据库要实时同步到redis,意味着要多部署12个组件,硬件和运维成本都会增加。

从server端到client端需要经过一次网络传输和序列化反序列化操作,然后再同步到接收端,感觉没有直接怼到接收端更高效。

go-mysql-transfer是使用Go语言实现的MySQL数据库实时增量同步工具, 参考Canal但是规避了上述三点。旨在实现一个高性能、低延迟、简洁易用的Binlog增量数据同步管道, 具有如下特点:

不依赖其它组件,一键部署

集成多种接收端,如:Redis、MongoDB、Elasticsearch、RocketMQ、Kafka、RabbitMQ,不需要再编写客户端,开箱即用

内置丰富的数据解析、消息生成规则;支持Lua脚本,以处理更复杂的数据逻辑

支持监控告警,集成Prometheus客户端

高可用集群部署

数据同步失败重试

全量数据初始化

二、 与同类工具比较

98f1245f66ddf10b89ef5a5f3705effb.png

三、 设计实现

1、实现原理

go-mysql-transfer将自己伪装成MySQL的Slave,向Master发送dump协议获取binlog,解析binlog并生成消息,实时发送给接收端。

643fc6ccb4ef556ad7ed63ca70493577.png

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表,数据如下:

4d49f717b9e9f1b1d652bb6e4696ff33.png

同步到Redis后,数据如下:

f1732b385ebeb8b5e90f96b6a9b6634b.png

更多规则配置和同步案例 请见后续的"使用说明"章节。

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表,数据如下:

4d49f717b9e9f1b1d652bb6e4696ff33.png

同步到Redis后,数据如下:

87d3257052dc2c48807c015cb1103961.png

更多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节点迅速替补上去,实现秒级故障切换。

5ea291ae30bd91eba42a0ee38d76e246.png

相关配置:

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,在控制台可以直观的看到数据同步状态,如下:

2a62fda7577b2614459056a3e0c8fd9d.png

四、安装

二进制安装包

直接下载编译好的安装包: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: 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,结果如下:

89e2537fe59c045934c8418dfc0c808a.png

3次运行的中间值为4.6秒

4、测试用例二

使用Lua脚本,将52万条数据全量初始化同步到Redis,结果如下:

ee56896d85dcec1fb2c6cbd3c96ab2a0.png

3次运行的中间值为9.5秒

5、测试用例三

使用规则,将binlog中52万条增量数据同步到Redis。结果如下:

683daf363ddd1804ebe1dddbf158141e.png每秒增量同步(TPS)32950条

6、测试用例四

使用Lua脚本,将binlog中52万条增量数据同步到Redis。结果如下:

f38c348e659adff38233103214403604.png

每秒增量同步(TPS)15819条

7、测试用例五

100个线程不停向MySQL写数据,使用规则将数据实时增量同步到Redis,TPS保持在4000以上,资源占用情况如下:

9a8e5745ee64a050022a3b58ecbbe160.png

100个线程不停向MySQL写数据,使用Lua脚本将数据实时增量同步到Redis,TPS保持在2000以上,资源占用情况如下:

71907b045c9730804e235ddb75b07874.png

以上测试结果,会随着测试环境的不同而改变,仅作为参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/297361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何备份服务器日志到其他服务器_KIWI Syslog日志服务器搭建及配置

认地,kiwi使用UDP 514端口接收日志数据,安装成功后即可接收日志使用命令netstat –ano查看服务器监听状态,如果服务没起来,则重新启动服务Kiwi Syslog Daemon任务:把当天的日志保存在G:event,历史日志保存在…

oncontextmenu事件

用oncontextmenu事件单禁用右键菜单 onconTextmenuwindow.event.returnValuefalse;右键菜单禁用&#xff0c;用这个可以禁止复制。 在<body>中加入属性代码&#xff1a; οncοntextmenu"return false" onselectstart"return false" …

【C++ Primer学习笔记】第2章:变量和基本类型

类型是所有程序的基础。类型告诉我们数据代表什么意思以及可以对数据执行哪些操作。 C中对类型的支持是非常广泛的&#xff1a;语言自身定义了一组基础类型和修改已有类型的方法&#xff0c;还提供了一组特征用于自定义类型。 2.1基本内置类型 wchar_t类型用于扩展字符&#xf…

Android 系统文件夹功能(转)

打开Android文件管理器&#xff0c;里面数十个英文名称命名的文件夹罗列其中&#xff0c;很多功能我们可以从其名字上略有所知&#xff0c;内部大批量的文件却让我们有些一头雾水。这些文件是什么?有什么用?我们能不能删?这些都是我们脑中充满疑问的。今日笔者就将Android手…

Dapr牵手.NET学习笔记:状态管理进阶(二)

为了防止并发对数据修改造成差异&#xff0c;dapr使用了etag标签来作为版本号&#xff0c;对数据修改进行验证。下面是对etag的一个demoappsettings.json中的url配置"StateUrl": "http://localhost:3500/v1.0/state/statestore"在PaymentSystem项目中添加两…

这三位同学的名字绝了!笑得肚子疼......

1 这三位同学的名字绝了&#xff01;▼2 我是一只小海豹我在假装跷跷板▼3 单身狗受到暴击▼4 小宝宝真可怜打针打出了条件反射▼5 每生成一个验证码都有一位程序员......▼6 虽然腿短但是人家跑得快啊▼7 内容引起极度舒适▼你点的每个赞&#xff0c;我都认真当成了喜欢…

python统计出现的中文标点_Python处理中文标点符号大集合

中文文本中可能出现的标点符号来源比较复杂&#xff0c;通过匹配等手段对他们处理的时候需要格外小心&#xff0c;防止遗漏。以下为在下处理中文标点的时候采用的两种方法:中文标点集合比较常见标点有这些&#xff1a;&#xff01;&#xff1f;&#xff61;&#xff02;&#x…

从Hadoop框架与MapReduce模式中谈海量数据处理(含淘宝技术架构)

从hadoop框架与MapReduce模式中谈海量数据处理前言几周前&#xff0c;当我最初听到&#xff0c;以致后来初次接触Hadoop与MapReduce这两个东西&#xff0c;我便稍显兴奋&#xff0c;认为它们非常是神奇&#xff0c;而神奇的东西常能勾起我的兴趣&#xff0c;在看过介绍它们的文…

WPF过渡面板

WPF开发者QQ群&#xff1a; 340500857 | 微信群 -> 进入公众号主页 加入组织欢迎转发、分享、点赞、在看&#xff0c;谢谢~。 前言效果投稿来源于-郑竣僖 QQ&#xff1a;41130958301—效果预览效果预览&#xff08;更多效果请下载源码体验&#xff09;&#xff1a;一、Tra…

UScript中的Pow函数

这些天越来越发现数学的重要和妙趣了&#xff0c; 由于一些地方需要使用指数次幂来实现更好的效果&#xff0c; 想当然地去找pow函数&#xff0c;把Object和Actor翻了个底朝天 。。。 结果可想而知&#xff0c; 也正好&#xff0c;尝试了一把UScript和C的混编&#xff0c;但搜一…

PM与工程师

原文链接&#xff1a;http://firecacada.blog.163.com/blog/static/70743762011117114451722/ 过节前看到一篇文章&#xff0c;讲产品项目就应该由工程师来主导&#xff0c;但国内让PM去驱动项目&#xff0c;搞得乱七八糟&#xff0c;很恼火&#xff0c;怎么可能做出一款好产品…

哪些才是对女朋友的有效关心,来学习一下~

1 这本书真好&#xff0c;在哪里才能买得到呢&#xff1f;2 求求你&#xff0c;不要抢我的小毛虫&#xff01;3 所有的乘客请系好安全带4 小朋友非常可爱了5 字幕组真的尽力了&#xff01;6 学习了7 10月16日&#xff0c;平安南京 根据真实接警经历&#xff0c;发布一则警示案例…

fabric 转账_Fabric 学习笔记-架构初探

本文介绍了Fabric的架构&#xff0c;以及通过一个简单的Demo来熟悉整个交易流程。Hyperledger fabric V1.0的架构如下图所示&#xff1a;application提供各种语言的SDK接口。membership也就是fabric-ca提供成员服务&#xff0c;用来管理身份&#xff0c;提供授权和认证。peer负…

多个goruntine 性能变慢_提高 JavaScript 性能的 12 个技巧

作者丨Liz Parody在创建 Web 应用程序时应始终考虑性能。为了帮助你开始&#xff0c;本文列举了有效提高应用程序性能的 12 种方法。性能是创建网页或应用程序时最重要的一个方面。没有人想要应用程序崩溃或者网页无法加载&#xff0c;或者用户的等待时间很长。根据 Kissmetric…

#celery#周期性任务

2019独角兽企业重金招聘Python工程师标准>>> 玩了一个星期的clannad&#xff0c;是时候干点事了。 折腾了下celery周期性任务&#xff1a; celery提供了一个叫celery beat的服务&#xff0c;用于定时驱使worker执行任务。也就是说&#xff0c;如果本地没有活动的wor…

WTMPlus 1.1 发布

点击上方蓝字关注我们1.1版本WTMPlus上线一个多月以来&#xff0c;得到了越来越多用户的喜爱。虽然1.0版本还有不少问题&#xff0c;但是它的核心理念和功能受到了用户的认可。1.1版本修复了大量用户反馈的易用性的问题和系统Bug&#xff0c;并且加入了强悍的图表工具。图表编辑…

backgroundworder 简单使用

//手动创建backgroundworker组件///创建 private backgroundworker bgd new backgroundworker(); ///声明xmldoment private xmldoment doment null; /// Load 加载事件 private void Form1_Load(object sender , EventArgs e) { /// 执行操作 this.bgd.DoWorker delegate(ob…

4部“教材级”纪录片,有生之年必看系列!

全世界只有3.14 % 的人关注了爆炸吧知识纪录片是以真实生活为创作素材&#xff0c;以真人真事为表现对象&#xff0c;并对其进行艺术的加工与展现的&#xff0c;以展现真实为本质&#xff0c;并用真实引发人们思考的电影或电视艺术形式。好的纪录片就像打开了一扇新世界的大门&…

mysql数据恢复时显示多线程恢复_MySQL多线程备份恢复工具mydumper,myloder

mydumper备份时&#xff0c;数据和表结构是分开的&#xff0c;比如备份test表时&#xff0c;会生成test.sql文件和test.schema.sql文件。myloader导数据的时候默认不会记录二进制日志&#xff0c;即使你的sql_log_binon.准备安装介质&#xff1a;下载mydumper-0.6.2.tar.gz下载…

IM实现联系人及联系人分组的数据库设计

openfire中通过ofgroup/ofgroupprop/ofgroupuser实现组织结构中的分组&#xff0c;通过ofroster/ofrostergroups实现好友关系中的分组。但是呢&#xff0c;我们做二次开发的时候&#xff0c;希望能够有自己的个人联系人及分组&#xff0c;它没有确认过程&#xff0c;也就是说&a…