Flink如何清理键控状态?

时间:2020-04-29 17:29:14

标签: apache-flink flink-streaming

在考虑通过某种键进行锁的行为时,我通常会想到将所有与该键匹配的事件扔到同一存储桶中的类比。可以想象,当Flink应用程序开始处理大量数据时,选择键入的内容开始变得很重要,因为您要确保正确清理状态。这使我想到了一个问题,Flink究竟如何清理这些“桶”?如果存储桶为空(所有MapStates和ValueStates为空),Flink是否会关闭键空间的该区域并删除存储桶?

示例:

传入数据格式:{userId,computerId,amountOfTimeLoggedOn}

键:用户ID /计算机ID

当前密钥空间:

  • Alice,计算机10:其中有2个事件。这两个事件都存储在状态中。
  • 鲍勃,计算机11:其中没有事件。什么都不会存储在状态中。

Flink会最终从密钥空间中删除Bob,Computer 11还是永久存在,因为在某个时刻它有一个事件吗?

2 个答案:

答案 0 :(得分:1)

Flink至少在现有的状态后端(堆(在内存中)或RocksDB)中不为没有任何用户值的状态键存储任何数据。

密钥空间在Flink中是虚拟的,Flink不对可能存在的具体密钥做任何假设。每个密钥或密钥子集没有任何预分配的存储桶。仅当用户应用程序为某个键写入某个值时,它才会占用存储空间。

通常的想法是,所有具有相同键的记录都在同一台计算机上处​​理(有点像您所说的在同一存储桶中)。某个密钥的本地状态也始终保持在同一台计算机上(如果存储的话)。不过,这与检查点无关。

例如,如果某个时间为[Bob,计算机11]写入了某个值,然后又将其删除,则Flink将使用键将其完全删除。

答案 1 :(得分:0)

简短回答

它借助Flink State的“生存时间” (TTL)功能和Java垃圾收集器(GC)进行清理。 TTL功能将删除对状态条目的任何引用,并且GC将收回分配的内存。

长答案

您的问题可以分为3个子问题:

我会尽量简短。

Flink如何基于Key对数据进行分区?

对于操作员通过键控流进行操作,Flink借助Consistent Hashing Algorithm在键上对数据进行分区。它创建max_parallelism个存储桶。每个操作员实例都分配了一个或多个这些存储桶。每当向下游发送数据时,会将密钥分配给这些存储桶之一,然后将其发送给相关的操作员实例。 此处未存储任何键,因为范围是通过数学计算得出的。因此,任何时间都不会清除任何区域或删除存储桶。您可以创建所需的任何类型的密钥。不会在键空间或范围方面影响内存。

Flink如何使用密钥存储状态?

所有运算符实例都有一个实例级状态存储。该存储区定义了该运算符实例的状态上下文,并且它可以存储多个命名状态存储区,例如“ count”,“ sum”,“ some-name”等。这些命名状态存储是键值存储,可以存储基于数据键的值。

当我们在操作员的open()函数中使用状态描述符初始化状态时,会创建这些KV存储。即getRuntimeContext().getValueState()

这些KV存储区仅在需要在状态中存储某些内容时才存储数据。 (如HashMap.put(k,v))。 因此,除非调用状态更新方法(例如updateaddput),否则不会存储任何键或值。

所以

  • 如果Flink没有看到密钥,则不会为该密钥存储任何内容。
  • 如果Flink看过密钥但未调用状态更新方法,则该密钥不存储任何内容。
  • 如果调用状态更新方法作为键,则键值对将存储在KV存储中。

Flink如何清除密钥的状态?

除非用户要求或用户手动完成,否则Flink不会删除状态。如前所述,Flink具有该状态的TTL功能。该TTL将标记状态到期,并在调用清除策略时将其删除。这些清除策略会更改后端类型和清除时间。对于堆状态后端,它将从状态表中删除该条目,即删除对该条目的任何引用。 Java GC将清除此未引用条目占用的内存。对于RocksDB状态后端,它仅调用RocksDB的本机delete方法。