在flink cdc同步数据时,基于sql的实现方式中发现了作业DAG有个SinkMaterializer
算子,而且检查checkpoint历史时发现该算子state越来越大,
有必要搞清楚为什么会多了这个算子,作用又是什么。
通过算子名称定位到了源码为类org.apache.flink.table.runtime.operators.sink.SinkUpsertMaterializer
,这个算子将输入的记录以upsert key作区分保存到state中,
并为下游算子提供一下upsert视图。
An operator that maintains incoming records in state corresponding to the upsert keys and generates an upsert view for the downstream operator.
单纯看类注释和代码逻辑,并不能理解它的用处及设计背景。
SinkUpsertMaterializer
是为了解决changelog流事件乱序造成了结果不正确的问题。
在分布式环境中,join, aggregate等操作经常会触发数据shuffling,可能会将source端同一主键记录的changelog分散到不同的下游算子中处理,造成数据处理乱序。
-- CDC源表:
event: event_id BIGINT, dim_id BIGINT, PRIMARY KEY(event_id)
dim: dim_id BIGINT, name VARCHAR, PRIMARY KEY(dim_id)
-- 结果表:
result: event_id BIGINT, dim_id BIGINT, name VARCHAR, PRIMARY KEY(event_id)
INSERT INTO result SELECT event.*,dim.name from event JOIN dim ON event.dim_id = dim.dim_id
两源表数据如何,event表只有1条数据,其中dim_id的值由10更新为11,所以整个流中产生了3条changelog数据。dim表中有dim_id为10,11的两条数据,没发生过修改。
event | dim |
---|---|
(+I,event_id=1,dim_id=10) (-U,event_id=1,dim_id=10) (+U,event_id=1,dim_id=11) |
(+I,dim_id=10,name=dim10) (+I,dim_id=11,name=dim11) |
当event表和dim表根据dim_id进行关联时,changelog数据将以dim_id为upsert keys进行shuffling,得到以下情况
由于sink接收的数据来自两个上游算子,由于网络或者是处理速度原因,sink最终接收到数据的顺序并不确定,唯一能确定的是+I会在-U之前,因为两者具有相同的dim_id(10),最终会被同一个join task顺序处理,
即最终sink的数据可能是以下几种可能:
情况一 | 情况二 | 情况三 |
---|---|---|
(+I,event_id=1,dim_id=10,name=dim10) (-U,event_id=1,dim_id=10,name=dim10) (+U,event_id=1,dim_id=11,name=dim11) |
(+I,event_id=1,dim_id=10,name=dim10) (+U,event_id=1,dim_id=11,name=dim11) (-U,event_id=1,dim_id=10,name=dim10) |
(+U,event_id=1,dim_id=11,name=dim11) (+I,event_id=1,dim_id=10,name=dim10) (-U,event_id=1,dim_id=10,name=dim10) |
SinkUpsertMaterializer
位于sink算子之前,它通过将上游乱序数据以**upsert keys分区缓存在state中,同时为下游的sink提供一个正确的upsert视图。
根据代码逻辑梳理出以下流程图
SinkUpsertMaterializer
处理逻辑:
单纯从代码逻辑很难理解,结合上述的示例,看看会得到什么效果。
分析情况二和情况三两种情况在SinkUpsertMaterializer
中会产生什么效果
乱序的-U最终在SinkUpsertMaterializer
中就被丢了,并不会发送到sink,而最后发到sink的是+U,最终状态与state保持一致。
情况三中,+I与+U乱序,-U与+U乱序,但是最终sink接收到的最后都是+U,数据正确。
table.exec.sink.upsert-materialize
配置项用于控制该算子的使用
推断的逻辑位于代码org.apache.flink.table.planner.plan.optimize.program.FlinkChangelogModeInferenceProgram.SatisfyUpdateKindTraitVisitor#analyzeUpsertMaterializeStrategy
当开启后,需要考虑state持续增大的情况,ttl受
table.exec.state.ttl
控制
https://blog.csdn.net/qq_32727095/article/details/129876631
https://www.ververica.com/blog/flink-sql-secrets-mastering-the-art-of-changelog-event-out-of-orderness