Flink 的状态都包含了哪些信息
在 Apache Flink 中,状态(State)是流处理引擎的核心概念之一。简单来说,状态就是“跨多个事件或时间窗口需要被记住的信息”。
Flink 的状态不仅包含了用户定义的业务数据,还包含了 Flink 引擎为了保证精确一次(Exactly-Once)语义、容错和扩缩容而维护的底层系统元数据。
具体来说,Flink 的状态包含了以下几个维度的信息:
一、 业务逻辑层面的信息(用户数据)
这是开发者最常接触的部分,主要用于记录计算的中间结果或历史上下文。根据 Flink 提供的不同数据结构,它包含的信息形式如下:
- ValueState
:保存单个值。例如:某个用户的账户余额、某个设备的最新温度、一个用来标记是否已经发送过报警的布尔标志(Boolean Flag)。 - ListState
:保存一个列表。例如:某个用户在过去一小时内的所有点击记录、一个时间窗口内收集到的所有事件。 - MapState<K, V>:保存键值对映射。例如:某个用户在不同商品类目下的购买次数(K=类目,V=次数)。
- ReducingState
/ AggregatingState<IN, OUT> :保存聚合后的结果。例如:累加的销售总额、平均响应时间(每次新数据到来时,与现有状态合并,而不是把所有历史数据都存下来)。
二、 按照作用域划分的信息
根据状态绑定的对象不同,状态中包含的信息分为两类:
- Keyed State(键控状态):
- 包含信息:与特定 Key 相关联的独立信息。只有经过
keyBy()之后的流才能使用。 - 例子:用户 A 的购物车数据、用户 B 的购物车数据(A 和 B 的状态互相隔离)。
- 包含信息:与特定 Key 相关联的独立信息。只有经过
- Operator State(算子状态):
- 包含信息:与某个算子的某个并行子任务(Sub-task)相关联的信息,与具体的 Key 无关。
- 例子:
- Kafka 偏移量(Offsets):Flink Kafka Consumer 会将当前读取到的各个分区的 Offset 保存在 Operator State 中,以便故障恢复时从断点继续读取。
- 广播规则(Broadcast State):一种特殊的算子状态,保存全局统一的配置或规则信息(如动态更新的黑名单),分发给所有并行的算子。
三、 系统级别的元数据(隐藏信息)
除了纯粹的业务数据,Flink 的底层状态后端(State Backend,如 RocksDB、HashMap)还会为了管理这些状态而保存额外的元数据信息:
- TTL(Time-To-Live)元数据:
- 如果开发者为状态配置了 TTL(存活时间),状态中不仅会保存业务数据本身,还会额外保存该状态的最后修改时间戳或最后访问时间戳。Flink 会定期检查这些时间戳,清理过期的数据。
- Key Group 元数据(用于扩缩容):
- 对于 Keyed State,Flink 并不是按照单独的 Key 来独立分配状态的,而是将 Key 映射到不同的 Key Group 中。状态底层包含了所属的 Key Group 索引信息,这样在改变算子并行度(扩缩容)时,Flink 可以以 Key Group 为单位进行状态的重新分配。
- 定时器状态(Timer State):
- 在处理 Event Time 或 Processing Time 时,开发者注册的定时器(Timers)本身也是一种状态。定时器队列(如到了某时刻要触发某个计算逻辑)会被保存在状态中,并参与 Checkpoint。这样即使机器宕机重启,定时器也不会丢失。
四、 Checkpoint / Savepoint 中的状态信息
当 Flink 触发 Checkpoint(检查点)时,内存或本地磁盘(RocksDB)中的状态会被序列化并保存到持久化存储(如 HDFS、S3)中。持久化文件里不仅包含上述数据,还包含全局元数据:
- Job ID 和算子 ID:用于识别这部分状态属于哪个作业的哪个算子。
- 状态的大小和偏移量:用于在恢复时快速定位并读取各个子任务的状态数据。
- 快照的 Barrier 信息:用于实现分布式快照的一致性。
总结
概括地说,Flink 的状态包含了:
- 过去发生的事情(历史事件列表、聚合值、标志位)。
- 外部系统的读取进度(如 Kafka Offsets)。
- 未来的触发计划(定时器队列)。
- 系统管理所需的元数据(TTL时间戳、Key Group 路由信息)。