回撤流是flink Table/SQL api处理动态表(流表)的独特机制,为了保证计算的正确性上游算子会发送回撤流给下游算子
回撤流是由一对记录组成:
撤回记录:表明当前记录值失效
新增记录:表示新的有效结果
举一个例子,我们对订单表统计一个用户的有效订单的总金额计算,其中status=1代表有效,订单流是一个变更流,status状态会发生改变,也会其他属性的更新或消息重复,这说明一个订单可能有多条记录
所以根据上面的场景如果我们想正确的统计用户有效订单的总金额,需要先对订单流进行开窗去重复,在进行统计
create table order_source () with{...}//省略创建source
//去重复
create view valid_order_detail as
select * from (
select
* ,
row_number() over ((PARTITION BY order_id ORDER BY update_time DESC) as rnk
from order_source
) as a
where a.rnk = 1
and a.status = 1
//计算用户有效订单的总金额
create view user_order_amount as
select
o.user_id,
sum(o.amount)
from
valid_order_detail as o
group by o.user_id
如果订单流来两条记录
| user_id | order_id | amount | status | update_time |
|---|---|---|---|---|
| u_1 | o_1 | 10 | 1 | 1711421644000 |
| u_1 | o_2 | 10 | 0 | 1711421643000 |
去除重复后
row_number算子只会下发
u_1 | o_1 | 10 | 1 | 1711421644000
此时 统计的结果
u_1 | 10
当来一条新纪录的时候,又将状态更新成0
u_1 | o_1 | 10 | 0 | 1711421645000
这个时候row_number算子就需要下发一个回撤流,通知下游聚合算子重新计算
-(u_1 | o_1 | 10 | 1 | 1711421644000)
+(u_1 | o_1 | 10 | 0 | 1711421645000)
统计的结果就会修正为正确的
在这个例子中我了解的回撤的基本原理,那么到底是谁产生回撤消息,而又是谁消费回撤消息?
简单来讲,基于key的动态表动态更新的场景下需要回撤,所谓动态表就是具有组件的source表或者join/group by阐述的动态表也就是例子中的valid_order_detail和
user_order_amount,
算子分为模式,分别是Accumulating Mode (AccMode) 和 Accumulating and Retracting (AccRetractMode)
AccMode只处理 acc message 而AccRetractMode的算子可以处理回撤消息
所以flink会根据上游的动态表来决定算子的模式。在例子中rank 算子和count算子都是AccRetractMode的算子。