MySQL binlog 增量数据解析服务

MySQL binlog 增量数据解析服务

介绍如何借助于 MySQL 的 Master-slave 协议实现 MySQL 增量数据获取服务

1. 起因

做过后端开发的同学都知道, 经常会遇到如下场景:

  1. 后端程序根据业务逻辑, 更新数据库记录
  2. 过了几天, 业务需求需要更新搜索索引
  3. 又过了几天, 随着数据需求方的增多, 结构改成发送数据到消息中间件(例如 Kafka), 其他系统自行从消息中间件订阅数据
传统程序结构

所有涉及到类似需求的代码中都写了各种发送消息中间件的代码, 冗余, 易错, 而且难以保证一致性. 那么问题来了:

数据都在 MySQL 中, 是否可以实现仅仅更新 MySQL 就实现数据更新和发布逻辑?

2. Linkedin Databus

最早我听说的解决方案是 Linkedin 实现的, 参见

核心思路就是通过数据库的 binary log(简称: binlog) 来实现数据库更新的自动获取. Linkedin 自己实现了 MySQL 版本和 Oracle 版本。

3. 原理

以 MySQL 为例, 数据库为了主从复制结构和容灾,都会有一份提交日志 (commit log),通过解析这份日志,理论上说可以获取到每次数据库的数据更新操作。获取到这份日志有两种方式:

  1. 在 MySQL server 上通过外部程序监听磁盘上的 binlog 日志文件
  2. 借助于 MySQL 的 Master-Slave 结构,使用程序伪装成一个单独的 Slave,通过网络获取到 MySQL 的binlog 日志流

这里有一个注意的点: MySQL 的 binlog 支持三种格式:StatementRowMixed 格式:

  • Statement 格式就是说日志中记录 Master 执行的 SQL
  • Row 格式就是说每次讲更改的数据记录到日志中
  • Mixed 格式就是让 Master 自主决定是使用 Row 还是 Statement 格式

由于伪装成 Slave 的解析程序很难像 MySQL slave 一样通过 Master 执行的 SQL 来获取数据更新,因此要将 MySQL Master 的 binlog 格式调整成 Row 格式才方便实现数据更新获取服务

至于 Oracle 的实现,我厂没用 Oracle。。。。

4. 数据增量同步服务拆解

好了, 如果想自己写一个 Databus 服务, 就需要如下几个核心模块:

  • 4.1、MySQL binlog 解析类库
  • 4.2、部署方式
  • 4.3、binlog 状态维护模块
  • 4.4、消息中间件(大多数人会选择 Kafka 吧)
  • 4.5、数据发布策略
  • 4.6、数据序列化方式
    • 将获取到的 binlog 序列化成其他可识别格式
    • AVRO、protocol buffer、JSON,哪个喜欢选哪个,但注意跨平台,别用 Java 原生的序列化 =.=|||
  • 4.7、集群管理服务
  • 4.8、服务监控

4.1、协议解析可选方案

时至今日, 已经有很多大厂开源了自己的 MySQL binlog 解析方案,Java 语言可选的有:

想自己造轮子实现协议的,也可以参考 MySQL 官方文档

4.2、部署方式

由于 binlog 可以通过网络协议获取,也可以直接通过读取磁盘上的 binlog 文件获取, 因此同步服务就有两种部署方式:

  • 通过读取 binlog 文件的话, 就要跟 MySQL Master 部署到同一台服务器
    • 系统隔离性不好,高峰期会不会跟 MySQL master 争抢系统资源
    • 类似 AWS RDS 这种云数据库服务,不允许部署程序到 RDS 节点
  • 通过 relay-log 协议通过网络读取,同步服务就方便部署到任意地方
部署方式

4.3、binlog 状态维护模块

在 MySQL 中, Master-slave 之间只用标识:

  1. serverId:master一般设置为1, 各个 server 之间必须不同
  2. binlog 文件名称:当前读取到了哪一个 binlog 文件
  3. binlog position:当前读取的 binlog 文件的位置

由于同步服务会重启,因此必须自行维护 binlog 的状态。一般存储到 MySQL 或者 Zookeeper 中。当服务重启后,自动根据存储的 binlog 位置,继续同步数据。

4.4、消息中间件可选方案

虽然现在 Kafka 如日中天,大多数情况下大家都会选择 Kafka 作为消息中间件缓冲数据。选择其他的消息中间件也未尝不可。 但有一点注意:

  • MySQL 中的数据更新是有顺序的
  • 数据更新发布到消息中间件中,也建议能够保序,例如事务中经典的转账的例子,试想一下如果消息队列不保序, 其他数据服务消费到不保序的数据是否还能满足业务需求

由于上诉原因,类似 AWS SQS 这样的消息队列就不满足此处对消息队列的需求(参见:AWS SQS 官方文档关于保序方面的解释

4.5、数据发布策略

解析到了数据,现在要做的就是将数据发布到消息中间件中。有一下几个方面需要注意:

4.5.1、topic 策略

一个 MySQL 节点中可以有多个数据库, 每个数据库有多张表,是采用一个节点一个 Kafka Topic,还是一个数据库一个 Topic, 还是一张表一个 Topic?

4.5.2、数据分区策略

Kafka 中数据是根据 key 进行分区, 同一个分区下保证消息的顺序。

如何选择数据的key的限制因素就是看数据消费端是否希望同一个表的同一条数据的更新记录都落到同一个 Kafka 分区上,进而不需要消费端做多进程间的状态维护, 简化消费端逻辑。例如: 一个Kafka Topic 有20个分区,同一个表 table_1 中 ID 为1的数据前后两次更新被发送到了不同的 partition,这就要求消费端必须每个 partition 保持lag一致, 并且及时同步数据状态到其他消费进程可见才可以保证保序; 但如果同一个表 table_1 中 ID 为1的数据前后两次更新被发送到了同一个 partition, 由于 Kafka 保证同一个 partition 保序,消费端就简化了很多。

如下图展示数据乱序问题:

  • 假设 kafka 中 A2 为新的数据, A1 为同一个 ID 的老数据
  • 由于 慢消费进程数据堆积,导致 A2 这个新数据先被消费, 当老数据A1被消费时有可能覆盖之前的结果
    数据乱序问题

要实现上述的逻辑, 就要求在 Kafka 数据的 Key 的选择上做文章:

  1. 一种方式是使用 table 的名称作为 Kafka 的 key,这样同一张表的数据一定在一个 partition 上保序。 但这样的坏处是,如果数据集中在某一张表频繁更新,会造成某一个 partition 上数据量远大于其他 partition,消费端无法通过并行方式提高扩展性。
  2. 另一种方式就是,在 db 层面保证每张表的第一个 column 是主键,这样采用 binlog 中第一个 column 的数据作为 Kafka 的 key, 数据的平衡性会好很多,易于消费端扩容。

如下图,消息无乱序情况:

  • 数据 AC 的每个版本由于 Hash 值 % 分区数量相同,同属于同一个分区, 并且按数据版本保序
  • 数据 BDAC, 数据按修改时间顺序保序但属于不同分区

4.6、数据序列化方式选择

读取到 binlog 数据后, 需要将数据序列化成更简单易用的格式,发送到 Kafka。如果选择 Avro 作为序列化方式的话,可以考虑集成 Kafka 背后的公司 Confluent 提出的一个新的方法:Schema Registry,具体信息参见 Confluent 公司官网。

4.7、集群管理服务

随着业务的扩展,越来越多的 MySQL 接入了数据同步服务。运维管理的压力也就随之而来。因此可能最后系统演变成如下结构:

  • 独立一个集群管理程序,负责管理解析程序节点,分配任务
  • 各个解析程序启动后,首先在 Zookeeper 注册,然后领取同步节点任务,启动解析过程
  • 类似的任务管理结构很常见,比如 Storm 中 Nimbus 节点管理 worker 节点等。
集群管理

4.8 服务监控

服务的监控必不可少。除了基础的进程监控,数据同步服务的关键是 binlog 解析服务与 MySQL master 之间的延迟监控,避免在 MySQL 写入高峰期导致数据延迟,影响后面的数据消费服务。

获取延迟的方法也很简单:

  1. 在 MySQL master 上实行 SHOW MASTER STATUS 获取到 Master 节点当前的文件 ID 和 binlog 位置
  2. 获取同步服务当前处理的 binlog 文件 ID 和位置:
  3. 将相减的结果发送到监控服务(例如 open-falcon),后续根据需求报警
    • 一般文件 ID 相减结果 N 大于1, 表示同步服务已经落后 MySQL Master N 个文件,情况比较严重(除非是 MySQL Master 刚刚 rotate 新文件)
    • 文件 ID 相同,binlog 位置相减结果 M 就是相差的 binlog 文件大小, 单位: bytes
    • 此计算公式仅仅为近似估算,建议在差距持续一段时间(比如持续2分钟)的情况下再报警。

5、踩过的坑

Canal Blob 类型字段编码

由于 Canal 将 binlog 中的值序列化成了 String 格式给下游程序,因此在 Blob 格式的数据序列化成 String 时为了节省空间,强制使用了 IOS_8859_0 作为编码。因此,在如下情况下会造成中文乱码:

  1. 同步服务 JVM 使用了 UTF-8 编码
  2. BLOB 字段中存储有中文字符

参见:

// com.alibaba.otter.canal.parse.inbound.mysql.dbsync.LogEventConvert 第541行起:
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
    // fixed text encoding
    // https://github.com/AlibabaTech/canal/issues/18
    // mysql binlog中blob/text都处理为blob类型,需要反查table
    // meta,按编码解析text
    if (fieldMeta != null && isText(fieldMeta.getColumnType())) {
        columnBuilder.setValue(new String((byte[]) value, charset));
        javaType = Types.CLOB;
    } else {
        // byte数组,直接使用iso-8859-1保留对应编码,浪费内存
        columnBuilder.setValue(new String((byte[]) value, ISO_8859_1));
        javaType = Types.BLOB;
    }
    break;

总结

通过实现数据同步服务,可以在一定程度上实现数据消费端与后端程序解耦。但凡事皆有成本,是否值得引入到现有系统架构中,还需要架构师自己斟酌。

-- EOF --

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容