Flink中的状态管理

Flink 使用状态来实现有状态的流处理。

1. 状态

在 Flink 中,状态State)用于记录应用在运行过程中,算子operator)的中间计算结果或者元数据信息

运行中的 Flink 应用如果需要上次计算结果进行处理的,则需要使用状态存储中间计算结果。如 Join、窗口聚合场景。

有状态操作的一些例子:

  • 对数据流中的重复数据去重,需要记录已经出现过的数据。当新数据流入时,需要根据记录的数据进行去重。
  • 检查数据流是否符合某个特定的模式,需要记录之前流入的数据。比如判断一个温度传感器数据流中的温度是否在持续上升。
  • 对一个时间窗口内的数据进行聚合分析,需要记录属于该窗口的数据。比如计算过去一小时内接口响应时间的 99 分位数。
  • 在线机器学习场景下,使用状态保存模型参数的当前版本,并根据新流入数据更新模型参数。

Flink 的一个算子有多个子任务,每个子任务分布在不同实例上。我们可以将 state 理解为某个算子子任务在当前实例上的一个变量,它存储了数据流的历史信息。当新数据流入时,我们可以结合历史信息进行计算。

实际上,Flink 的 state 是由算子子任务来创建和管理的。一个 state 更新和获取的流程如下图所示:一个算子子任务接收输入流,获取对应的 state,根据新的计算结果更新 state

2. 状态类型

Flink 有两种基本类型的状态,托管状态(managed state)和原生状态(raw state)。

Managed State

Raw State

由谁管理

Flink Runtime 托管,自动存储、自动恢复、自动伸缩

开发者自己管理,需要自己进行序列化

支持的数据结构

支持多种数据结构:ListState、MapState 、ValueState等

只支持字节数组:byte[]

使用场景

绝大多数 Flink 算子

用户自定义算子

一般情况下我们使用 Managed State 就可以了,而Managed State可以分为Keyed StateOperator State

  • Keyed StateKeyedStream 上的状态,不同的 key 对应的不同的状态。
  • Operator State 则是和操作符Operator)的一个特定的并行实例绑定。

keyed state

operator state

适用场景

只适用于KeyedStream上的算子

适用于所有算子

状态分配方式

每个 key 对应一个状态

每个算子子任务对应一个状态

横向扩展/扩缩容

状态随着 keyBy 的分组 KeyGroup 自动在多个算子子任务上迁移

以 KeyGroup 为单位重分配

有多种状态重新分配的方式,均匀分配或者广播分配

创建和访问方式

自定义算子(重写 RichFunction,通过 State 名称从 getRuntimeContext 方法创建或获得 state)

实现 CheckpointFunction 等接口

支持数据结构

ValueStateListStateMapStateReducingStateAggregatingStateFoldingState

ListState、BroadcastState 等

3. StateBackend

StateBackend定义了状态是如何存储的,不同的 State Backend 会采用不同的方式来存储状态。目前 Flink 提供了三种不同形式的StateBackend,分别是 MemoryStateBackend, FsStateBackendRocksDBStateBackend,三者对比如下:

MemoryStateBackend

FsStateBackend

RocksDBStateBackend

可存储对象

operator/keyed state

operator/keyed state

keyed state

状态存储位置

TaskManager JVM 堆内存

TaskManager JVM 堆内存

RocksDB

存取速度

快,存取需要访问堆上的对象

快,存取需要访问堆上的对象

慢,存取需要序列化和反序列化

存取大小

受限于JVM堆内存大小,可能导致 OOM

受限于JVM堆内存大小,可能导致 OOM

支持保存TB级别的state,上限取决于磁盘大小

支持快照方式

异步全量

异步全量

异步全量、异步增量

state 持久化位置

JobManager JVM 堆内存

文件系统

文件系统

一般而言,在生产环境中,我们会在 FsStateBackendRocksDBStateBackend 间选择:

  • FsStateBackend:性能更好;日常存储是在堆内存中,面临着 OOM 的风险,不支持增量checkpoint
  • RocksDBStateBackend:无需担心 OOM 风险,是大部分时候的选择

4. 状态持久化

对于MemoryStateBackendFsStateBackend来说,状态存储在 TaskManagerJVM 堆内存中;对于RocksDBStateBackend来说,状态存储在TaskManagerManagedMemory 中,如果 ManagedMemory 不够存储时,RocksDB 使用磁盘进行存储。

总而言之,状态存储在内存中,虽然满足了存取速度快的要求,但同时也面临着丢失的风险。所以,Flink 引入了状态快照的概念,定期将状态进行持久化。

状态持久化是通过 checkpoint 完成的,所以状态持久化也就是 checkpoint 的存储,可以分为:

  • 基于内存的全量 checkpoint
  • HDFS 全量 checkpoint
  • 基于RocksDB的全量/增量 checkpoint

不管使用了哪种state backend,容错都是由CheckpointStorage来管理的,它定义了在流应用中为了实现容错state backend如何保存状态。

Flink定期获取每个操作符的所有状态的持久快照,并将这些快照复制到更持久的地方,比如分布式文件系统。如果发生故障,Flink可以恢复应用程序的完整状态,并恢复处理过程,就像没有发生任何错误一样。存储这些快照的位置叫做CheckpointStorage

CheckpointStorage有两种实现:一种将其状态快照持久化到分布式文件系统,另一种使用JobManager的堆内存。

JobManagerCheckpointStorage

FileSystemCheckpointStorage

保存位置

JobManager的堆内存

文件系统,比如 HDFS,S3, NFS, GCS

优点

轻量级、不需要额外的依赖

支持 TB 级别的状态大小;高可用

缺点

不可扩展,只能保存很小的状态

适用场景

本地开发和测试

生产部署

StateBackendCheckpointStorage的对应关系:

  • MemoryStateBackend使用JobManagerCheckpointStorage
  • FsStateBackendRocksDBStateBackend使用FileSystemCheckpointStorage

5. 总结

有了状态之后,Flink 可以实现有状态的流处理。

有了状态之后,Flink 可以跨并行实例重新分配状态,来实现 Flink 应用程序的扩缩容。

Flink 提供了不同的state backends,用于指定如何存储状态,以及在何处存储状态。

State Backendcheckpoint的关系如下图:

  • 38
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值