为什么基于 RocketMQ 进行业务库数据同步时会消息乱序?

203 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

前言

最近大数据组需要同步业务数据库的订单表的全部数据,如果让数据组直接跑大SQL在订单系统的数据库上来拉去数据,是会严重影响订单系统的性能的。所以我们采用了Canal这个中间件去监听mysql的binlog日志,并发送到mq中去,数据组就可以监听mq拉去binlog日志来实现数据同步。

问题点

在实际开发中,会发现binlog的日志是
insert into order values(xx, 0)
update order set xxvalue=100 where id=xxx
就是先插入了一条订单数据,刚开始给一个默认值是0,接着再更新成100。
然后这两条SQL语句是对应着两个binlog的,也就是两个更新日志,一个binlog是insert语句的,一个binlog是update语句的,这个binlog会进入到MQ中去。
然后数据组从MQ获取出来binlog的时候,居然是先获取出来了update语句的binlog,然后再获取了insert语句的binlog
也就是说,这个时候会先执行更新操作,但是此时数据根本不存在,没法进行更新,接着执行插入操作,也就是插入一条字段值为0的 订单数据进去,最后大数据存储中的订单记录的字段值就是0。

原因

因为RocketMQ每个Topic可以指定多个MessageQueue,当写入消息的时候,其实是会把消息均匀分发给不同的MessageQueue的。
比如我们这里在写入binlog到MQ的时候,可能会把insert binlog写入到一个MessageQueue里去,update binlog写入到另外一个MessageQueue里去。
因为RocketMQ只能保证同一个MessageQueue下的顺序,在不同MessageQueue下自然是无法保证顺序的。

处理方法

可以将Topic设置成一个MessageQueue,可以保证顺序,但是会有消息积压的风险。
也可以利用RocketMQ的特性,分区顺序消息来实现。具体原理和操作将在下一篇详细展开。