Canal 发送到kafka多分区保证数据结果一致性2021-02-12

 

一、mq顺序性问题

1. canal目前选择支持的kafka/rocketmq,本质上都是基于本地文件的方式来支持了分区级的顺序消息的能力,也就是binlog写入mq是可以有一些顺序性保障,这个取决于用户的一些参数选择

2. canal支持MQ数据的几种路由方式:单topic单分区,单topic多分区、多topic单分区、多topic多分区

canal.mq.dynamicTopic,主要控制是否是单topic还是多topic,针对命中条件的表可以发到表名对应的topic、库名对应的topic、默认topic name

canal.mq.partitionsNum、canal.mq.partitionHash,主要控制是否多分区以及分区的partition的路由计算,针对命中条件的可以做到按表级做分区、pk级做分区等

3. canal的消费顺序性,主要取决于描述2中的路由选择,举例说明:

单topic单分区,可以严格保证和binlog一样的顺序性,缺点就是性能比较慢,单分区的性能写入大概在2~3k的TPS

多topic单分区,可以保证表级别的顺序性,一张表或者一个库的所有数据都写入到一个topic的单分区中,可以保证有序性,针对热点表也存在写入分区的性能问题

单topic、多topic的多分区,如果用户选择的是指定table的方式,那和第二部分一样,保障的是表级别的顺序性(存在热点表写入分区的性能问题),如果用户选择的是指定pk

hash的方式,那只能保障的是一个pk的多次binlog顺序性 ** pk
hash的方式需要业务权衡,这里性能会最好,但如果业务上有pk变更或者对多pk数据有顺序性依赖,就会产生业务处理错乱的情况.
如果有pk变更,pk变更前和变更后的值会落在不同的分区里,业务消费就会有先后顺序的问题,需要注意

4. canal.mq.partitionHash 表达式说明

canal 1.1.3版本之后, 支持配置格式:schema.table:pk1^pk2,多个配置之间使用逗号分隔

例子1:test\\.test:pk1^pk2 指定匹配的单表,对应的hash字段为pk1 + pk2

例子2:.*\\..*:id 正则匹配,指定所有正则匹配的表对应的hash字段为id

例子3:.*\\..*:$pk$ 正则匹配,指定所有正则匹配的表对应的hash字段为表主键(自动查找)

例子4: 匹配规则啥都不写,则默认发到0这个partition上

例子5:.*\\..* ,不指定pk信息的正则匹配,将所有正则匹配的表,对应的hash字段为表名   按表hash:
一张表的所有数据可以发到同一个分区,不同表之间会做散列 (会有热点表分区过大问题)

例子6: test\\.test:id,.\\..* , 针对test的表按照id散列,其余的表按照table散列

二、Canal 写入kafka多分区解决办法

背景:

为了提升之后 kafka 对数据库表数据同步的吞吐量,我们需要对相应的kafka topic 创建多个分区,这样就可以有多个kafka 消费者组同时对kafka数据消费,提高同步速率,避免数据积压。

问题:

如果使用 Canal 配置文件 instance.properties 的默认配置:

canal.mq.partition=0
#hash partition config
#canal.mq.partitionsNum=3
#canal.mq.partitionHash=test.table:id^name,.*\\..*
canal会把从 binlog 解析后的数据只发送到 kafka 的一个分区。

而我们的需求是将数据发送到 kafka 的多个分区,并且数据在从kafka消费的时候不会因为多个 comsumergroup 消费顺序的不同,对数据的最终结果产生影响。(如,同一条数据,本来是先insert 后 delete,结果 delete 先处理,之后才处理insert 就会造成数据错误)

解决:

可以通过修改canal的 instance.properties 配置,让同一id的数据进入kafka的同一个分区,比如,我们可以将数据按照 article_id 进行 hash 后分发到不同的分区,这样,article_id相同的数据就会进入同一分区,之后消费者组在消费数据的时候就会按照 aritcle_id 的实际操作顺序进行消费,保证了数据最终的一致性。

canal.mq.partition=0
#hash partition config
canal.mq.partitionsNum=3
#canal.mq.partitionHash=test.table:id^name,.*\\..*
canal.mq.partitionHash=canaltest\\.test:article_id

猜你喜欢

转载自blog.csdn.net/u011250186/article/details/113796793