在最近的项目中有使用到Flink 批处理的方式来进行数据源的数据同步。由于一些原因,并没有去使用开源的flinkx 之类的工具,而且Flink Jdbc connect 是一种基于查询数据同步,在数据同步之前必然会业务方造成查询压力的。目前在需要同步的表中,其它的表数据最高也不超过千万,但是我们源表的数据量却是达到了上亿级别。这种数据量的业务,在数据查询中一不小心就会照成查询超时,所以在大表数据同步时需要特别注意。而由于 Flink jdbc 本身设计原因,它默认是不支持条件查询的,默认均是扫描全表。所以最后是自己自定义数据源的方式来进行数据同步。
最开始的时候,我是使用分页的方式来进行数据的分段查询。这样的好处是可以减少数据库的查询压力,当主键是整形时 采用id between 方式来进行数据查询,当主键是string 时采用 limit 方式进行查询。
最终发现,当主键是自增的整形时,效果最佳。速率也最快,而当主键非整形时,会发现前面数据的limit 查询时速度还是可以,但是当数据量达到百万级别以上时,查询速率就会开始下降,表越大 后面分页查询的速率也越慢。
解决办法:
针对这种主键非整形的数据查询,我们可以采用mysql 游标方式进行数据查询,数据源设置 FetchSize 为 Integer.MIN_VALUE 只有设置这个值,数据读取的速度才能像流一样快。
那当数据源变成流的方式读取后,我们又会发现 sink 也需要整改,因为流的数据是一条条的,我们不可能是一条条的插入,只有批量插入效率才高。这个时候我们就会想到flink 的窗口函数,因为这里我并不需要水印时间 所以我这边使用的是TumblingProcessingTimeWindows 窗口。
那当我们使用这种窗口函数的时候我们总会遇到一个问题,特别是水印窗口的时候,如果数据的水印迟迟无法更新,那么窗口就无法触发计算。那给人的感觉就好像数据丢失的感觉。关于这个问题我们可以参考 https://blog.csdn.net/shirukai/article/details/117700828
那在批处理的时候使用窗口函数还有一个更关键的问题,那就是当上游数据源结束的时候,下面的窗口函数就会默认流程已经结束了,就不会再生成新的窗口,那这样的后果就是会导致最后一批数据会丢失 无法入库。
解决办法:
延迟数据源的关闭,可以在查询数据完毕后 再设置个线程休眠时间,保证最后的窗口能正常生成。
总结:
1、数据源的主键最好使用自增型分布式id,例如雪花id,这样的好处是在于在数据同步的时候可以多并行度的读取
2、数据源的主键是非整形的情况下,使用分页查询进行数据查询的话,后期会出现分页数据重复的情况。因为非整形主键的索引分布是随机的,当在数据查询的时候有新数据插入时,就会偶发分页查询重复问题
3、使用replace into 代替 insert into 避免数据重复导致数据插入失败
4、在批处理中使用时间窗口函数需要注意上游数据源的结束时间,当上游数据源结束时,下游将不会继续生成窗口,为避免下游数据丢失需要延迟上游数据源的结束时间