Flink中的处理状态 - Working with State

Flink中有两种基本状态:Keyed State和Operator State

定义:
	实际上就是指flink计算中间的计算结果或者元数据属性等信息,State会做相应的持久化
	说白了:State就是Flink计算过程中与时间相关的内部数据的快照,用于checkpoints做容错

为什么需要:
	基于各种原因,导致需要使用checkpoints去从state中做恢复
		
何时启动:
	在使用checkpoint时

工作机制

键控状态-Keyed State

Keyed State 始终与键有关,并且只能在KeyedStream上的函数和运算符中使用。可以将 Keyed State 视为已分区或分片的Operator State 每个键只有一个状态分区。每个keyed-state在逻辑上绑定到的唯一组合,由于每个键“belogs”键操作符的一个并行实例,所以我们可以简单地把它看作是

键控状态(Keyed State)进一步组织成所谓的键组。键组是一个原子单元,通过它Flink可以重新分配键控状态;作为最大并行度的键组是和监控状态的数量是一样多。在执行过程中,每个键操作符的并行实例处理一个或多个键组的键。

操作符状态-Operator State

使用操作符状态(或非键控状态),每个操作符状态都绑定到一个并行操作符实例。Kafka连接器是在Flink中使用操作符状态的一个很好的例子。Kafka使用者的每个并行实例都维护一个主题分区和偏移量的映射作为其操作符状态。

当并行度改变时,操作符状态接口支持在并行操作符实例之间重新分配状态。进行这种重新分配可以有不同的方案。

呈现形式

原始和托管状态-Raw and Managed State

Keyed State and Operator State有两种形式:托管Managed 状态和原始Raw 状态

托管状态由Flink运行时控制的数据结构表示,如内部散列表或RocksDB。例如“ValueState”、“ListState”等。Flink在运行时对状态进行编码并将它们写入检查点

原始状态是操作符在其自己的数据结构中保持的状态。当检查点时,它们只向检查点写入一个字节序列。Flink对状态的数据结构一无所知,只看到原始字节

所有datastream函数eg:stream.keyby()都可以使用托管状态,但是原始状态接口只能在实现操作符时使用建议使用托管状态(而不是原始状态),因为通过托管状态,当并行性发生变化时,Flink能够自动重新分配状态,而且还可以进行更好的内存管理。

注意:如果您的托管状态需要自定义序列化逻辑,请参阅相应的指南以确保未来的兼容性。Flink的默认序列化器不需要特殊处理

状态生存时间-State Time-To-Live (TTL)

生存时间(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允许在清理之前返回过期状态。

  • 状态后端存储最后一次修改的时间戳和用户值,这意味着启用该特性会增加状态存储的消耗。堆状态后端在内存中存储一个附加的Java对象,其中包含对用户状态对象的引用和一个原始长值。RocksDB状态后端为每个存储值、列表条目或映射条目添加8个字节。
  • 目前只支持涉及处理时间(processing time)的ttl
  • 如果试图使用启用了TTL的描述符或启用了TTL的描述符恢复先前在没有TTL的情况下配置的状态,将导致兼容性失败和statmigration异常
  • TTL配置不是检查或保存点的一部分,而是Flink在当前运行作业中处理它的一种方式
  • 只有当用户值序列化器能够处理空值时,带有TTL的映射状态当前才支持空用户值。如果序列化器不支持空值,则可以使用NullableSerializer对其进行包装,代价是序列化形式中多出一个字节

清除过期状态

默认情况下,过期值只有在显式读出时才会被删除,例如,通过调用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

你可能感兴趣的:(Flink)