在考虑通过某种键进行锁的行为时,我通常会想到将所有与该键匹配的事件扔到同一存储桶中的类比。可以想象,当Flink应用程序开始处理大量数据时,选择键入的内容开始变得很重要,因为您要确保正确清理状态。这使我想到了一个问题,Flink究竟如何清理这些“桶”?如果存储桶为空(所有MapStates和ValueStates为空),Flink是否会关闭键空间的该区域并删除存储桶?
示例:
传入数据格式:{userId,computerId,amountOfTimeLoggedOn}
键:用户ID /计算机ID
当前密钥空间:
Flink会最终从密钥空间中删除Bob,Computer 11还是永久存在,因为在某个时刻它有一个事件吗?
答案 0 :(得分:1)
Flink至少在现有的状态后端(堆(在内存中)或RocksDB)中不为没有任何用户值的状态键存储任何数据。
密钥空间在Flink中是虚拟的,Flink不对可能存在的具体密钥做任何假设。每个密钥或密钥子集没有任何预分配的存储桶。仅当用户应用程序为某个键写入某个值时,它才会占用存储空间。
通常的想法是,所有具有相同键的记录都在同一台计算机上处理(有点像您所说的在同一存储桶中)。某个密钥的本地状态也始终保持在同一台计算机上(如果存储的话)。不过,这与检查点无关。
例如,如果某个时间为[Bob,计算机11]写入了某个值,然后又将其删除,则Flink将使用键将其完全删除。
答案 1 :(得分:0)
它借助Flink State的“生存时间” (TTL)功能和Java垃圾收集器(GC)进行清理。 TTL功能将删除对状态条目的任何引用,并且GC将收回分配的内存。
您的问题可以分为3个子问题:
我会尽量简短。
对于操作员通过键控流进行操作,Flink借助Consistent Hashing Algorithm在键上对数据进行分区。它创建max_parallelism
个存储桶。每个操作员实例都分配了一个或多个这些存储桶。每当向下游发送数据时,会将密钥分配给这些存储桶之一,然后将其发送给相关的操作员实例。 此处未存储任何键,因为范围是通过数学计算得出的。因此,任何时间都不会清除任何区域或删除存储桶。您可以创建所需的任何类型的密钥。不会在键空间或范围方面影响内存。
所有运算符实例都有一个实例级状态存储。该存储区定义了该运算符实例的状态上下文,并且它可以存储多个命名状态存储区,例如“ count”,“ sum”,“ some-name”等。这些命名状态存储是键值存储,可以存储基于数据键的值。
当我们在操作员的open()
函数中使用状态描述符初始化状态时,会创建这些KV存储。即getRuntimeContext().getValueState()
。
这些KV存储区仅在需要在状态中存储某些内容时才存储数据。 (如HashMap.put(k,v)
)。 因此,除非调用状态更新方法(例如update
,add
,put
),否则不会存储任何键或值。
所以
除非用户要求或用户手动完成,否则Flink不会删除状态。如前所述,Flink具有该状态的TTL功能。该TTL将标记状态到期,并在调用清除策略时将其删除。这些清除策略会更改后端类型和清除时间。对于堆状态后端,它将从状态表中删除该条目,即删除对该条目的任何引用。 Java GC将清除此未引用条目占用的内存。对于RocksDB状态后端,它仅调用RocksDB的本机delete
方法。