flinksql 回撤流中主键发生变更的影响(group by中的值发生改变)

本文探讨了Flink SQL在处理回撤流时主键变更的问题,分析了当上游数据包含回撤信息时,如何影响下游数据处理。在某些场景下,主键变更会导致数据更新错误,需要通过特定策略如行号标记来触发回撤。此外,文章还讨论了实时数仓架构中应对数据变化的策略,包括Holo的分字段更新和实时流与离线流的联合处理,并指出了各自的优缺点。

1 什么是回撤流

这篇文章主要谈论一个场景,简单来说:
首先我们来简单的说一下什么是回撤流,以及回撤流的底层原理,举个例子:
在这里插入图片描述

这个说的不是很清晰 ,其实倒数第二个图当作输出 比较好看出来,再bank 这一条来之后,数据结果是
hello 1
world 1
bank 1
这个时候 又来了一条hello,刚刚已经下发的hello 1 这个结果就不对了,就需要降这条结果撤回,也就是撤回一条 hello 1 下发一条 hello 2。简单的说 这就是回撤流。

我们用最常见的 ,用的最多的kafka来举例回撤流 大家可能就明白了

  • 来了一条hello数据 下发一条
    [+I] hello 1
  • 又来了一条hello,上一条撤回
    [-U] hello 1
  • 下发一条
    [+U] hello 2

其实所谓的回撤,是下发了两条数据,告诉下游 上一条我废弃了,后面这条才是现在有效的,下游所有支持回撤流的存储引擎,就会做处理,最常见的就是mysql,对于mysql来说,就是发生了一次update,来完成了这次回撤流

2 主键变更场景

那么这里引申出一个新的场景,我们接受到的数据,在之前的假设中,都是新插入数据,每一条都是新数据对吧,那如果上游数据

### Flink 回撤介绍 在 Flink 中,当使用 Flink SQL 处理实时数据,把表转化成时,有 `toAppendStream` 与 `toRetractStream` 两个方法。回撤是实时生成的,使用 `toRetractStream` 方法可以处理它。回撤中的数据带有操作标识,通常用 `+I` 表示插入(Insert),`-U` 表示更新前的回撤(Update Retract),`+U` 表示更新后的数据(Update Insert)。例如: ``` +----+--------------------------------+-------------+ | op | ipv4 | value1 | +----+--------------------------------+-------------+ | +I | 10.10.28.240 | 100 | | -U | 10.10.28.240 | 100 | | +U | 10.10.28.240 | 101 | | -U | 10.10.28.240 | 101 | | +U | 10.10.28.240 | 102 | | -U | 10.10.28.240 | 102 | | +U | 10.10.28.240 | 103 | ``` 这里的 `+I` 表示新插入一条数据,`-U` 表示要将之前的某条数据回撤,`+U` 表示更新后的数据插入 [^1][^2]。 ### Flink 回撤处理方法 #### 自定义 Sink 实现 不依赖官方的 `flink - jdbc` 实现,自己实现一个 Sink 来处理回撤数据。在自定义 Sink 中,对回撤数据进行特殊处理,确保回撤数据和更新数据能正确写入目标存储。以下是一个简单的 Java 示例: ```java import org.apache.flink.configuration.Configuration; import org.apache.flink.streaming.api.functions.sink.RichSinkFunction; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; public class CustomMySQLSink extends RichSinkFunction<YourRecordType> { private Connection connection; private PreparedStatement insertStmt; private PreparedStatement deleteStmt; @Override public void open(Configuration parameters) throws Exception { super.open(parameters); // 建立数据库连接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/your_database", "username", "password"); // 插入语句 insertStmt = connection.prepareStatement("INSERT INTO your_table (column1, column2) VALUES (?, ?)"); // 删除语句 deleteStmt = connection.prepareStatement("DELETE FROM your_table WHERE column1 = ?"); } @Override public void invoke(YourRecordType value, Context context) throws Exception { if (value.isRetract()) { // 处理回撤数据 deleteStmt.setString(1, value.getColumn1Value()); deleteStmt.execute(); } else { // 处理正常数据 insertStmt.setString(1, value.getColumn1Value()); insertStmt.setString(2, value.getColumn2Value()); insertStmt.execute(); } } @Override public void close() throws Exception { super.close(); if (insertStmt != null) { insertStmt.close(); } if (deleteStmt != null) { deleteStmt.close(); } if (connection != null) { connection.close(); } } } ``` #### 状态管理 利用 Flink 的状态管理机制,维护数据的状态。在接收到回撤数据时,更新状态信息,然后根据最新的状态信息将数据写入目标存储。这样可以确保数据的一致性。 #### 批量写入优化 为了提高写入性能,采用批量写入的方式。在自定义 Sink 中,缓存一定数量的数据,当数据达到一定数量或者达到一定时间间隔时,批量写入目标存储。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Direction_Wind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值