减轻事件时间偏差如何减少检查点故障

简介

Apache Flink 提供对事件时间和有状态事件处理的支持,这使其与其他流处理器区分开来。但是,在各种源以不同速度随时间进展的情况下,使用事件时间可能会导致检查点失败。本文描述了导致这些问题的原因,如何检查您的 Flink 作业是否存在这种情况,并提出了缓解该问题的解决方案。

了解检查点

当定期触发检查点时,系统会生成关于 Flink 作业中所有有状态运算符的一致快照。这需要制定执行计划。有状态运算符的快照彼此对齐这一事实使其保持一致。此对齐过程从 Flink 在作业源的输入通道中注入所谓的检查点屏障开始。

了解水印

除了流中的常规数据事件之外,还有 Flink 注入流中的所谓“系统事件”。此类系统事件的示例包括检查点障碍(如上一段所述),以及水印。Apache Flink 使用水印来跟踪事件时间的进度。事件时间是从数据事件的字段之一中提取的,该字段包含最初创建该事件时的时间戳。通常,会生成水印并将其添加到源处的流中。重要的是要认识到,每个单独的源都会生成自己的水印并将其添加到流中。因此,在任何给定的时间点,系统中不只有一个事件时间,而是与源(的实例)一样多。

什么会导致事件时间偏差

事件时间偏差是操作符的各个实例的水印随着时间的推移彼此相距较远的结果。事件时间偏差的原因之一是当 Flink 作业需要使用来自具有不同特征的源的事件时。让我们以以下一组源为例:

  • 一个源每小时加载一个事件
  • 另一个源每秒加载 10K 个事件
  • 最后,具有不频繁的高突发事件的源

随着时间的推移,使用此源组合将导致各自的水印进展彼此显着不同。在 Flink 作业失败后,这种情况通常会被放大,作业需要赶上读取事件。

事件时间偏差的另一个原因是数据本身的分布不平衡。例如,某些键可能比其他键更频繁地出现,使得处理此类键的相应运算符只需进行更多处理,这可能会导致水印进展变慢。

由事件时间偏差引起的问题

许多 Flink 作业都使用与窗口相关的函数和/或键控操作,其中计时器在有状态运算符确定发出什么内容以及何时发出内容的过程中发挥着重要作用。这些计时器带有 anonTimer( …)仅当运算符的水印达到或超过这些计时器的时间戳时才调用的方法。考虑到此类操作符使用最低水位线来触发其计时器,很明显,事件时间偏度会导致许多副作用,即:

  • 待处理计时器的数量将会增加,与它们相关的资源量也会增加
  • 背压正在增加
  • 进度检查点屏障的速度会减慢,并且整个检查点过程将花费更长的时间,导致输入流被阻塞,从而进一步减慢事件的处理速度

这些副作用最终可能导致内存不足错误、检查点故障甚至作业崩溃。

减少事件时间偏差

由于事件时间偏差有多种原因,因此有多种解决方案可以应用。从 Flink 1.11 开始,对所谓的空闲检测提供了开箱即用的支持。如果一个主题在较长一段时间内不产生事件并因此导致没有水印进展,则该主题被称为空闲。 Flink 提供的解决方案是,当在可配置的时间段内没有接收到任何事件时,将此输入源标记为“暂时空闲”,从而导致在确定算子的最低水印时忽略此源。

如何检测事件时间偏差

了解此处描述的问题是否发生在您的 Flink 作业中最可靠的方法是查看每个 Flink Kafka 消费者分配的 Kafka 分区的滞后。不幸的是,这些指标不可用。当然可以查看所有 Kafka 分区的滞后,但由于您不知道这些分区是如何分配给所有 Flink Kafka 消费者实例的,因此很难得出任何结论。因此,您需要根据一些间接指标得出自己的结论,例如检查总检查点时间的增长是否快于状态大小的增长,或者状态运算符的各个实例之间的检查点确认时间是否存在差异。后者可能部分是由于您的数据分布不均匀造成的。最可靠的指标可能是 Flink Kafka Consumer 实例的不规则水印进展。

平衡读取来救援

以平衡的方式读取 Kafka 主题中的事件将极大地缓解 Kafka Consumer 的默认行为导致的问题。我们使用的平衡读取算法也被称为 K-way merge。K-way merge(或平衡读取)可以应用于两个级别:第一个是单个 Kafka 消费者实例中指定主题的平衡读取,而第二个包括所有 Kafka 消费者实例所需的协调。我们将在下面的段落中介绍这两个级别。

单个 Kafka 消费者内的平衡读取

单个 Kafka 消费者内的平衡读取可以通过利用其所谓的“消费流控制”机制来实现。此机制使您能够在轮询周期内暂时暂停使用某些分区的事件,然后再恢复使用事件。要知道何时暂停和何时恢复,需要更改 Flink Kafka 消费者的算法,如下所示:

  1. 最初消费者尝试读取所有分配的分区。
  2. 如果在第一个轮询周期中无法读取某些已分配的分区,则已读取的分区将暂停,以确保剩余的已分配分区将在下一个轮询周期或此后的任何轮询周期中读取。
  3. 在此过程中,我们将对来自所有分区的所有事件进行排队,并且此时我们不会发出任何内容。
  4. 只有当所有分区都被读取时,我们才能通过在所有非空闲主题分区上建立最高水印来确定必须发出什么。
  5. 用于组装要发出的事件集合的算法是所谓的 K 路合并算法。该算法使用 K 排序列表作为输入 – 在我们的例子中,来自指定分区的消费事件根据时间戳进行过滤,直到最高的常见水印。结果是所有分区中按事件时间排序的平衡事件列表。发出此排序列表将导致水印逐渐进展。
  6. 然后重复此过程,此时我们可以选择通过让算法计算每个轮询周期的“轮询超时”的最佳值来进一步优化它。

为 Flink 作业中的所有 Kafka 消费者实例平衡读取

到目前为止,我们已经解释了如何为单个 Kafka 消费者实例进行平衡读取。问题仍然是我们如何在 Flink 作业中的所有 Kafka 消费者的所有实例中执行此操作。我们在这里选择默认的 Fink 行为,其中具有多个输入流的 Flink 运算符管理其水印。然后通过背压间接管理,这可能会引入一些延迟,这比引入复杂的节点间通信更好。

结论

我们希望这篇文章能帮助您理解事件时间偏差是如何影响 Apache Flink 作业的,以及您可以采取哪些措施来减轻其影响。

关注公众号“大模型全栈程序员”回复“大数据面试”获取800页左右大数据面试宝典 ,回复“大数据”获取多本大数据电子书

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。更多免费资源在http://www.gitweixin.com/?p=2627