前言
最近大批量flink-cdc任务运行遇到一个报错,加班的罪恶源头~
server-id异常报错
Caused by: io.debezium.DebeziumException:
A slave with the same server_uuid/server_id as this slave has connected to the master;
the first event 'binlog.000002' at 1136, the last event read from './binlog.000002' at 1431, the last byte read from './binlog.000002' at 1431.
Error code: 1236; SQLSTATE: HY000. at io.debezium.connector.mysql.MySqlStreamingChangeEventSource.wrap(MySqlStreamingChangeEventSource.java:1146)
... 5 more
Caused by: com.github.shyiko.mysql.binlog.network.ServerException:
A slave with the same server_uuid/server_id as this slave has connected to the master;
the first event 'binlog.000002' at 1136, the last event read from './binlog.000002' at 1431,
the last byte read from './binlog.000002' at 1431.
at com.github.shyiko.mysql.binlog.BinaryLogClient.listenForEventPackets(BinaryLogClient.java:937)
... 3 more
报错原因
该报错是同一个库(相同实例)同步多个表时,启动了多个flink-cdc任务时出现的。
经排查造成的原因是:
来自:阿里云开发者社区 chengfengpolang
一开始看到这个解释还是一脸懵的,配置mysql binlog的时候有印象确实设置过server-id = 1,难道是启动两个相同的mysql实例的flink-cdc任务时如果都用相同的server id会导致这类冲突报错?带着这个疑问我寻找了一些资料…
自从flink cdc贡献到apache后,有些资料在旧官网已经看不到了,不过在新官网中找到了server-id的一些相关配置。
深入细节
于是打开flink-cdc-connector源码一探究竟
MysqlSource createReader -> MysqlSourceConfigFactory
在源码注释里面也是很清晰说明了:这个server-id如果不设置的话默认是从5400-6400中随机分配一个数字,建议用户设置一个具体的值;同时也说了如果没开启参数scan.incremental.snapshot.enabled,则只用指定一个值,如果开启了该参数,则需要指定一个范围并且不能小于source的并行度。
这里可以对这个参数做一些解释,在flink-cdc 2.x版本引入了一个重要特性之一:增量快照读取机制。
这种全选的读取方式支持并发读取,原理是根据表的主键将所有加载到的数据根据scan.incremental.snapshot.chunk.size(默认值是8096)进行切分,例如100w数据就是切分成 100w ÷ size 个chuck,然后这些chuck根据程序分配的并行度读取器并行读取,这样能做到snapshot阶段的多并行,且可以以chunk为粒度进行checkpoint
debezium官方文档
Debezium是flink-cdc的底层采集工具,其官方文档也是建议MySqlConnector实例的进程中server id唯一且需设置在范围1——232-1之内。
根据获取到的信息,我手动给他根据给这两个相同实例的任务设置不同的server id,程序1并行度是10,我将server id设置成1-11,程序2并行度是16,我将server id设置成12-28,程序果然能长时间正常运行,没有再出现结束snapshot阶段进入binlogReader阶段时报错server-id冲突。
事情到这里就结束了吗,并没有!!!
如果这个实例只是一两个任务,那手动设置server id还可以,如果这个实例有需求上百上千个任务,或者我想把它集成到数据集成平台中呢,我一个一个任务启动时还需要去代码中配置server-id,那大量的开发时间就会浪费掉!
一了百了
有没有自动化的方法呢?of course 这里我尝试了2个方法:
- 当前时间timestamp,范围(timestamp ~ timestamp + parallelism)
- 查询数据库里面的使用到的server-id,根据查询到的server-id进行排序,取每次查询到的max(server-id)取该值~max(server-id) + parallelism
果不其然,第一个方案很快就成功了。
System.currentTimeMillis() / 1000
但是有个弊端,由于毫秒超出server-id最大值范围,需要保证任务不是一次性批量启动的,要保证并行度的秒数内不同时启动才可以,比如第一个任务启动的是20并行度,第二个任务需要20秒后启动;
在尝试开启plan B的时候,遇到了比较多的问题,当我执行这个sql语句去查询数据库server-id时。
show variables like 'server_id'
查询出来的结果一直都是1(mysql binlog中设置的my.ini文件里的server-id值),无论程序中运行的server-id改成什么都记录不了,所以直接pass掉方案二。
如果还有什么其他比较好用的方法欢迎交流,创作不易,欢迎转发点赞支持~