Flink中有两种基本状态:Keyed State和Operator State
定义:
实际上就是指flink计算中间的计算结果或者元数据属性等信息,State会做相应的持久化
说白了:State就是Flink计算过程中与时间相关的内部数据的快照,用于checkpoints做容错
为什么需要:
基于各种原因,导致需要使用checkpoints去从state中做恢复
何时启动:
在使用checkpoint时
Keyed State 始终与键有关,并且只能在KeyedStream上的函数和运算符中使用。可以将 Keyed State 视为已分区或分片的Operator State 每个键只有一个状态分区。每个keyed-state在逻辑上绑定到
键控状态(Keyed State)进一步组织成所谓的键组。键组是一个原子单元,通过它Flink可以重新分配键控状态;作为最大并行度的键组是和监控状态的数量是一样多。在执行过程中,每个键操作符的并行实例处理一个或多个键组的键。
使用操作符状态(或非键控状态),每个操作符状态都绑定到一个并行操作符实例。Kafka连接器是在Flink中使用操作符状态的一个很好的例子。Kafka使用者的每个并行实例都维护一个主题分区和偏移量的映射作为其操作符状态。
当并行度改变时,操作符状态接口支持在并行操作符实例之间重新分配状态。进行这种重新分配可以有不同的方案。
Keyed State and Operator State有两种形式:托管Managed 状态和原始Raw 状态。
托管状态由Flink运行时控制的数据结构表示,如内部散列表或RocksDB。例如“ValueState”、“ListState”等。Flink在运行时对状态进行编码并将它们写入检查点。
原始状态是操作符在其自己的数据结构中保持的状态。当检查点时,它们只向检查点写入一个字节序列。Flink对状态的数据结构一无所知,只看到原始字节。
所有datastream函数eg:stream.keyby()都可以使用托管状态,但是原始状态接口只能在实现操作符时使用。建议使用托管状态(而不是原始状态),因为通过托管状态,当并行性发生变化时,Flink能够自动重新分配状态,而且还可以进行更好的内存管理。
注意:如果您的托管状态需要自定义序列化逻辑,请参阅相应的指南以确保未来的兼容性。Flink的默认序列化器不需要特殊处理。
生存时间(TTL)可以分配给任何类型的键控状态。如果配置了TTL,并且某个状态值已经过期,那么将尽最大努力清理存储值。
所有状态集合类型都支持每项TTLs。这意味着列表元素和映射项独立过期。
为了使用状态TTL,必须先构建StateTtlConfig配置对象。然后,可以在任何状态描述符中通过传递配置来启用TTL功能:
import org.apache.flink.api.common.state.StateTtlConfig
import org.apache.flink.api.common.state.ValueStateDescriptor
import org.apache.flink.api.common.time.Time
val ttlConfig = StateTtlConfig
.newBuilder(Time.seconds(1))
.setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
.build
val stateDescriptor = new ValueStateDescriptor[String]("text state", classOf[String])
stateDescriptor.enableTimeToLive(ttlConfig)
说明:
newBuilder方法的第一个参数是强制性的,它是生存时间值。
更新类型在状态TTL刷新时配置(默认情况下为OnCreateAndWrite):
StateTtlConfig.UpdateType.OnCreateAndWrite - only on creation and write access
StateTtlConfig.UpdateType.OnReadAndWrite - also on read access
(默认情况下NeverReturnExpired)如果未清除过期值,则状态可见性配置该值是否在读取访问时返回
StateTtlConfig.StateVisibility.NeverReturnExpired - expired value is never returned
StateTtlConfig.StateVisibility.ReturnExpiredIfNotCleanedUp - returned if still available
在NeverReturnExpired的情况下,即使它仍然需要被删除,过期状态的行为就好像它不再存在一样。该选项对于在TTL之后数据必须严格不可读和访问的用例非常有用,例如处理隐私敏感数据的应用程序。
另一个选项ReturnExpiredIfNotCleanedUp允许在清理之前返回过期状态。
默认情况下,过期值只有在显式读出时才会被删除,例如,通过调用ValueState.value()。
这意味着,默认情况下,如果未读取过期状态,则不会删除它,这可能会导致状态不断增长。在未来的版本中可能会有所改变。
全快照中的清理
此外,可以在获取完整状态快照时激活清理,这将减少其大小。在当前实现下,本地状态不会被清除,但在从上一个快照恢复时,它将不包括已删除的过期状态。可以在StateTtlConfig中配置:
import org.apache.flink.api.common.state.StateTtlConfig
import org.apache.flink.api.common.time.Time
val ttlConfig = StateTtlConfig
.newBuilder(Time.seconds(1))
.cleanupFullSnapshot
.build
此选项不适用于RocksDB状态后端的增量检查点。
对于现有的作业,这种清理策略可以在StateTtlConfig中随时激活或停用,例如在从保存点重新启动之后。
后台中清理
除了在全快照中清理,你还可以在后台激活清理。如果它被用于后端,下面的选项将激活StateTtlConfig中的默认后台清理:
import org.apache.flink.api.common.state.StateTtlConfig
val ttlConfig = StateTtlConfig
.newBuilder(Time.seconds(1))
.cleanupInBackground
.build