Flink 去重 Debezium-json 格式数据

背景

上游 Kafka 数据为 debezium-json 格式,由 Flink SQL 关联 Kafka Stream 和 Dim 表打宽写入,由于上有任务重启回到至同一条数据多次进行下游 kafka 导致下游 Flink Stream API 消费导致数据重复处理;
目前的数据格式为 debezium-json 格式,主要的标识符为 C 和 D 标识的数据(包括新增的 C 的数据,删除场景的 D 的数据以及更新场景下被拆分为 D 和 C 标识的数据)

去重思路

参考 Flink SQL 的去重思路,将数据根据唯一标识进行 KEY-BY 操作,并将进入的数据进行存储,根据数据标识 C 或者 D 进行不同逻辑处理:

  • D 数据进入:需要判断是否之前有处理输出 C 标记数据,如果有则可以将 D 数据直接输出,并且如果 C 数据输出条数大于 1 ,则需要移除一条存储的 C 标记状态并输出该 C 标记数据(即 UPDATE场景触发);如果 C 标记数据为 0,标识没有输出则 D 标记数据也不可输出,存入状态等待 C 标记数据进入处理;如果 C 标记数据为 1,则只需要删除存储的 C 标记状态(即 删除场景触发)。
  • C 数据进入:与 D 数据处理类似,C 数据可以抵消存储在状态中的 D 数据,如果数据第一次进入直接输出 C 并存储;如果 C 数据已有存储,则将当前进入的 C 数据进行存储不可输出(即 重复写入);如果 D 数据大于 0,则 C 和 D 同时输出并移除存储的一条 D 数据(即 更新场景)。

C 数据处理逻辑

未命名文件 (1).png

D 数据处理逻辑

未命名文件 (2).png

具体实现

public class DeduplicationFunction 
    extends KeyedProcessFunction<String, String, String> {

    // 存储当前ID数据是否已有输出
    ValueState<Boolean> isUniqueState;
    // D 标识数据存储
    ListState<String> deleteDataState;
    // C 标识数据存储
    ListState<String> createDataState;


    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        // 设置状态过期时间
        final StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(1))
                .neverReturnExpired()
                .updateTtlOnCreateAndWrite()
                .useProcessingTime()
                .build();

        final ValueStateDescriptor<Boolean> valueStateDescriptor =
                new ValueStateDescriptor<>("isUnique-state", Boolean.class);
        valueStateDescriptor.enableTimeToLive(ttlConfig);
        isUniqueState = getRuntimeContext().getState(
                valueStateDescriptor
        );
        final ListStateDescriptor<String> deleteDataStateDesc =
                new ListStateDescriptor<>("deleteDataState-state", String.class);
        deleteDataStateDesc.enableTimeToLive(ttlConfig);
        deleteDataState = getRuntimeContext().getListState(
                deleteDataStateDesc
        );
        final ListStateDescriptor<String> createDataStateDesc =
                new ListStateDescriptor<>("createDataState-state", String.class);
        createDataStateDesc.enableTimeToLive(ttlConfig);
        createDataState = getRuntimeContext().getListState(
                createDataStateDesc
        );
    }

    @Override
    public void processElement(String value, Context ctx, Collector<String> out) throws Exception {
        final JSONObject jsonObject = JSONObject.parseObject(value);
        final String op = jsonObject.getString("op");
        Boolean isUnique = isUniqueState.value();
            // isUnique 为 true 说明输出了 C 标识的数据
            if (null == isUnique || !isUnique) {
                // 如果 isUnique 状态为false,
                // 说明之前未处理过该数据,将该ID标记为处理过,
                // 并输出该数据
                if (op.equalsIgnoreCase("c")) {
                    // 如果当前数据是C类型
                    createDataState.add(value);
                    out.collect(value);
                } else if (op.equalsIgnoreCase("d")) {
                    // 如果当前数据是D类型, 为不可输出数据,存入状态
                    deleteDataState.add(value);
                }
                isUniqueState.update(true);
            } else {
                final List<String> createDataList = 
                    IteratorUtils.toList(createDataState.get().iterator());
                final List<String> delDataList = 
                    IteratorUtils.toList(deleteDataState.get().iterator());
                // 如果 isUnique 状态为 true ,说明之前已经处理过该数据
                if (op.equalsIgnoreCase("d")) {
                    // 如果当前数据是D类型
                    // 查找之前是否存在一个 C 操作的版本,
                    // 如果存在则输出之前的 C 操作数据,
                    // 否则将当前的 D 操作数据存储在 Keyed List State 中
                    if (createDataList.size() > 0) {
                        out.collect(value);
                        // 将当前 D 操作数据抵消掉之前积累的 C 
                        // 操作数据列表中的一个 C 操作数据
                        if (createDataList.size() > 1) 
                        	// 如果 C 标记数据大于 1 则从存储中移除一个并和 D 数据一起输出
                            // 可理解为 UPDATE 操作
                            final String remove = createDataList.remove(0);
                            // c 和 d 都输出
                            out.collect(remove);
                        } else {
                            // 移除之前存储的 C 状态数据
                            createDataList.remove(0);
                        }
                        createDataState.update(createDataList);
                    } else {
                        delDataList.add(value);
                        deleteDataState.update(delDataList);
                    }
                } else {
                    // C 标记数据
                    if (delDataList.size() > 0) {
                        // 将当前 D 操作数据抵消掉之前积累的 C 
                        // 操作数据 列表中的一个 C 操作数据
                        final String remove = delDataList.remove(0);
                        deleteDataState.update(delDataList);
                        // c 和 d 都输出
                        out.collect(remove);
                        out.collect(value);
                    } else if (createDataList.size() > 0) {
                        createDataList.add(value);
                        createDataState.update(createDataList);
                    } else {
                        // 当前 状态中 C 数据 和 D 数据都为0 即 相当于 isUnique = false 状态
                        // 所以需要将 C 数据存储
                        createDataState.add(value);
                        out.collect(value);
                    }
                }
            }
    }
}

flink-connector-debezium是一个在Apache Flink中使用的Debezium连接器。Debezium是一个开源的分布式事件流平台,专门用于捕获和推送针对数据库的更改事件。flink-connector-debezium连接器允许Flink应用程序使用Debezium来监控和获取数据库中的更改事件。 使用flink-connector-debezium,可以将DebeziumFlink无缝集成,实现实时和准确地捕获数据库的更改事件。当数据库中的数据发生变化时,Debezium可以将这些更改事件以流的形式推送到Flink应用程序。应用程序可以利用Flink提供的强大的流式处理功能,对这些更改事件进行实时的转换、计算和分析。这样可以快速获取数据库中的最新更改,使Flink应用程序能够及时响应并进行实时处理。 通过使用flink-connector-debezium,可以轻松地将数据库的更改事件流集成到Flink应用程序中,帮助实现实时的数据处理和分析任务。它提供了灵活且可靠的方法,用于捕获和传输数据库的变更事件,使得应用程序能够保持与数据库之间的实时同步。这对于需要对数据库进行实时处理和分析的企业应用程序非常有用,例如实时报表生成、实时监控、实时推荐系统等。 总而言之,flink-connector-debezium允许Flink应用程序利用Debezium捕获和处理数据库中的更改事件,从而实现实时的数据处理和分析任务。这是一个强大且灵活的工具,使得企业能够更好地利用实时的数据库变更事件来驱动他们的实时应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值