【源码】FlinkSQL 流式去重源码解析

目录

去重基类

处理时间的 First Row

处理时间的 Last Row

事件时间的去重

往期精选▼


 

本文基于 flink 1.12.0 版本进行源码解析

笔者之前在使用 FlinkSQL 的去重和TopN 功能时只是简单的看了下官网,然后用 sql 实现了功能,但是还有些疑问没有解决。

比如:去重算子占用的状态有多少?另外不使用 mini-batch 模式,去重的结果很单一,升序就一直输出最后一条(降序就只输出第一条数据)。

为了解决这些疑问,特意研究了下去重部分的源码。 去重部分源码类的结构图如下:

去重基类

DeduplicateFunctionBase 定义了去重的状态,由于是去重,所以只需要一个 ValueState 存储一个 Row 的数据就可以了(不管是处理时间还是事件时间)

// state stores previous message under the key. 基于key的状态用于去重
protected ValueState<T> state;

public DeduplicateFunctionBase(
        TypeInformation<T> typeInfo,
        TypeSerializer<OUT> serializer,
        long stateRetentionTime) {
    this.typeInfo = typeInfo;
    // 状态保留时间,决定去重的数据的去重窗口大小
    this.stateRetentionTime = stateRetentionTime; 
    this.serializer = serializer;
}

@Override
public void open(Configuration configure) throws Exception {
    super.open(configure);
    ValueStateDescriptor<T> stateDesc = new ValueStateDescriptor<>("deduplicate-state", typeInfo);
    // 设置去重状态的 ttl(非常重要)
    StateTtlConfig ttlConfig = createTtlConfig(stateRetentionTime);
    // 判断 ttl 是开启的
    if (ttlConfig.isEnabled()) {
        stateDesc.enableTimeToLive(ttlConfig);
    }
    // 创建用于去重的状态
    state = getRuntimeContext().getState(stateDesc);
}

处理时间的 First Row

ROW_NUMBER() OVER (PARTITION BY cate_id ORDER BY process_time asc) AS rownum

即取基于处理时间的第一条数据

处理类为:ProcTimeDeduplicateKeepFirstRowFunction

基于处理时间特性即后一条一定比前一条大这个逻辑,直接判断用于去重 的state.value 是否为空,如果为空则表示是第一条数据,直接下发;非空则说明前面该key数据存在而非第一条,此时不下发。 

public class ProcTimeDeduplicateKeepFirstRowFunction
        extends DeduplicateFunctionBase<Boolean, RowData, RowData, RowData> {

    private static final long serialVersionUID = 5865777137707602549L;

    // state stores a boolean flag to indicate whether key appears before.
    public ProcTimeDeduplicateKeepFirstRowFunction(long stateRetentionTime) {
        super(Types.BOOLEAN, null, stateRetentionTime);
    }

    @Override
    public void processElement(RowData input, Context ctx, Collector<RowData> out) throws Exception {
        // 调用处理时间的判断方法: DeduplicateFunctionHelper.processFirstRowOnProcTime
        processFirstRowOnProcTime(input, state, out);
    }
}

DeduplicateFunctionHelper.processFirstRowOnProcTime 函数如下:

static void processFirstRowOnProcTime(
            RowData currentRow,
            ValueState<Boolean> state,
            Collector<RowData> out) throws Exception {

        // 仅处理只包含插入行为的row,否则报错
        checkInsertOnly(currentRow);
        // ignore record if it is not first row
        //非空则说明前面该key数据存在而非第一条,此时不下发
        if (state.value() != null) {
            return;
        }
        // 添加第一次出现key对应的状态
        state.update(true);
        // emit the first row which is INSERT message
        // 下发第一次出现的key对应的row
        out.collect(currentRow);
    }
 

处理时间的 Last Row

ROW_NUMBER() OVER (PARTITION BY cate_id ORDER BY process_time desc) AS rownum

即取基于处理时间的最后一条数据

处理时间的逻辑同样基于后一条一定比前一条大这个逻辑,直接判断去重 state.value 是否为空,为空则表示是第一条数据,直接输出,不为空则前面有数据,判断是否更新上一条数据,并输出当前数据;

另外Last row 不同的地方是,如果接收的 cdc 数据源源,是可以支持删除前一条数据的(在本文中暂时不讨论)

public class 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值