3.4、Flink 集群部署(Deployment & Operations)- State & Fault Tolerance(Flink状态和容错)之 调优检查点和大状态

Table of Contents

监控State and Checkpoints

调优Checkpointing

网络缓存调优

异步Checkpointing

RocksDB调优

增量备份

RocksDB计时器

预定义选项

将选项工厂传递给RocksDB

容量规划

压缩

本地恢复任务

触发

捷径

主(分布式存储)和次(任务-本地)状态快照的关系

task-local 配置恢复

关于不同状态后端任务-本地恢复的详细信息

配置保存调度


要使 Flink 应用程序大规模可靠地运行,必须满足两个条件:

  • 应用程序需要能够可靠地接受检查点
  • 在失败之后,资源需要充分跟上输入数据流

第一部分讨论如何在大规模上很好地执行检查点。最后一节解释了一些关于计划使用多少资源的最佳实践。

监控State and Checkpoints

监视检查点行为的最简单方法是通过 UI 的检查点部分。检查点监视的文档显示了如何访问可用的检查点指标。

Flink Debugging & Monitoring - Monitoring Checkpointing(监控 Checkpointing 指标)

在扩展检查点时,有两个数字特别值得注意:

  • 算子开始检查点的时间:目前没有直接暴露,但对应于:checkpoint_start_delay = end_to_end_duration - synchronous_duration - asynchronous_duration 当触发检查点的时间非常长时,这意味着检查点 barrier 需要很长时间才能从源传输到算子函数中。这通常表明系统在恒定的背压下工作。
  • 在比对期间缓冲的数据量。对于精确的一次性语义,Flink 将流对齐到接收多个输入流的算子处,为这种对齐缓冲一些数据。理想情况下,缓冲的数据量较低——较高的数据量意味着从不同的输入流接收检查点屏障的时间差异很大。

请注意,当存在瞬态背压、数据倾斜或网络问题时,此处指示的数字可能偶尔很高。然而,如果数字一直很高,这意味着检查点占用了很多的 Flink 资源。

调优Checkpointing

检查点按应用程序可以配置的定期间隔触发。当检查点的完成时间比检查点间隔的时间长时,在进行中的检查点完成之前不会触发下一个检查点。默认情况下,下一个检查点将在正在进行的检查点完成后立即触发。

当检查点结束时通常花费的时间比基本间隔长(例如,因为状态比计划的大,或者检查点存储的存储空间暂时变慢),系统就会不断地接受检查点(一旦完成,新的检查点就会立即启动)。这可能意味着在检查点上有太多的资源被占用,而算子取得的资源太少。此行为对异步使用检查点状态的流应用程序影响较小,但仍可能对整个应用程序性能产生影响。

为了防止这种情况,应用程序可以定义检查点之间的最小持续时间:

StreamExecutionEnvironment.getCheckpointConfig().setMinPauseBetweenCheckpoints(milliseconds)

这个持续时间是最近一个检查点结束到下一个检查点开始之间必须经过的最小时间间隔。下图说明了这对检查点的影响。

3.4、Flink 集群部署(Deployment & Operations)- State & Fault Tolerance(Flink状态和容错)之 调优检查点和大状态_第1张图片

网络缓存调优

在 Flink 1.3之前,网络缓冲区数量的增加也会导致检查点时间的增加,因为保持更多的动态数据意味着检查点 barrier 会被延迟。从 Flink 1.3开始,每个传出/传入通道使用的网络缓冲区的数量是有限的,因此可以在不影响检查点时间的情况下配置网络缓冲区(请参阅网络缓冲区配置)。

异步Checkpointing

当状态异步快照时,检查点比状态同步快照时伸缩性更好。特别是在具有多个 join、co-function 或 window 的更复杂的流应用程序中,这可能会有很大的影响。

要异步存储状态,需要使用一个支持异步存储的状态后端。从 Flink 1.3开始,基于 rocksdb 和基于堆的状态后端(文件系统)都支持异步快照,并且默认使用它。这既适用于 managed operator state,也适用于 managed keyed state(包括 timers state)。

注意,RocksDB状态后端与基于堆的计时器的组合当前不支持计时器状态的异步快照。像键控状态这样的其他状态仍然是异步快照的。

RocksDB调优

许多大型 Flink Stream 应用程序的状态存储主要是 RocksDB 状态后端。后端可扩展到主内存之外,并可靠地存储大的键控状态。

缺点是,RocksDB 的性能可能会随着配置的不同而变化,而且几乎没有关于如何正确调优 RocksDB 的文档。例如,默认配置是针对 ssd 硬盘定制的,并且在旋转的磁盘上执行次优配置。

增量备份

与完整的检查点相比,增量检查点可以显著减少检查点时间,代价是(潜在的)更长的恢复时间。其核心思想是,增量检查点只记录对前一个已完成检查点的所有更改,而不是生成状态后端完整的、自包含的备份。就像这样,增量检查点建立在以前的检查点上。Flink 利用 RocksDB 的内部备份机制,以一种随时间推移而自我整合的方式。因此,Flink 中的增量检查点历史不会无限增长,旧的检查点最终会自动包含和修剪。

虽然我们强烈建议对大型状态使用增量检查点,但请注意,这是一个新特性,目前在默认情况下没有启用。为了启用这个特性,用户可以实例化一个 rocksdbstateback,并在构造函数中将相应的布尔标志设置为true,例如:

 RocksDBStateBackend backend =
        new RocksDBStateBackend(filebackend, true);

RocksDB计时器

对于 RocksDB,用户可以选择将计时器存储在堆(默认)中还是存储在 RocksDB 中。基于堆的计时器可以为更少的计时器提供更好的性能,而将计时器存储在 RocksDB 中可以提供更高的可伸缩性,因为 RocksDB 中的计时器数量可能超过可用的主内存(溢出到磁盘)。

当使用 Rock DB 作为状态后端时,可以通过 flink 的配置 state.backend.rocksdb.timer-service.factory 来选择定时器存储的类型。可选类型有:heap 和 rockdb。

RocksDB 状态后端基于堆的计时器的组合目前不支持计时器状态的异步快照。像键控状态这样的其他状态仍然是异步快照的。请注意,这不是以前版本的回归,将用FLINK-10026解决。

预定义选项

Flink 针对不同的设置为 RocksDB 提供了一些预定义的选项集合,有两种方法可以将这些预定义的选项传递给 RocksDB:

  • state.backend.rocksdb.predefined-options 默认值 DEFAULT,意思是 PredefinedOptions.DEFAULT
  • RocksDBStateBackend.setPredefinedOptions(PredefinedOptions.SPINNING_DISK_OPTIMIZED_HIGH_MEM)

随着时间的推移,我们希望积累更多这样的配置文件。当发现一组运行得很好并且似乎可以代表某些工作负载的选项时,请随意提供这些预定义的选项配置文件。

请注意,以编程方式设置的预定义选项将覆盖通过flink-conf.yaml配置的选项。

将选项工厂传递给RocksDB

在 Flink 中有两种方法将 options factory 传递给 RocksDB:

  • state.backend.rocksdb.options-factory 用来设置 option factory 的类名,默认值 org.apache.flink.contrib.streaming.state.DefaultConfigurableOptionsFactory,所有候选的可配置选项都在RocksDBConfigurableOptions 中定义。此外,还可以像下面这样定义定制的可配置选项工厂类,并将类名传递给 state.backend.rocksdb.options-factory。
  public class MyOptionsFactory implements ConfigurableOptionsFactory {

        private static final long DEFAULT_SIZE = 256 * 1024 * 1024;  // 256 MB
        private long blockCacheSize = DEFAULT_SIZE;

        @Override
        public DBOptions createDBOptions(DBOptions currentOptions) {
            return currentOptions.setIncreaseParallelism(4)
                   .setUseFsync(false);
        }

        @Override
        public ColumnFamilyOptions createColumnOptions(ColumnFamilyOptions currentOptions) {
            return currentOptions.setTableFormatConfig(
                new BlockBasedTableConfig()
                    .setBlockCacheSize(blockCacheSize)
                    .setBlockSize(128 * 1024));            // 128 KB
        }

        @Override
        public OptionsFactory configure(Configuration configuration) {
            this.blockCacheSize =
                configuration.getLong("my.custom.rocksdb.block.cache.size", DEFAULT_SIZE);
            return this;
        }
    }
  • RocksDBStateBackend.setOptions(new MyOptionsFactory())

以编程方式设置的选项工厂将覆盖通过 flink-conf 配置的选项工厂。如果进行了配置或设置,则与预定义选项相比,选项工厂具有更高的优先级。

RocksDB 是一个本地库,它直接从进程分配内存,而不是从 JVM 分配内存。任何分配给 RocksDB 的内存都必须考虑在内,通常是通过将 taskmanager 的JVM堆大小减少相同的数量。如果不这样做,可能会导致纱线/Mesos/等终止JVM进程,以分配比配置更多的内存。

容量规划

容量规划的基本原则:

  • 正常的算子应该在有足够的能力在恒定的被压下工作。
  • 在正常的资源下,提供一些额外的资源,在程序恢复期间保证输入数据的存储,这取决于恢复程序需要多长时间以及场景需要故障恢复的速度。(基于 checkpointing 启用的情况下,因为会占用一些资源,网络带宽)
  • 临时的背压通常是可以的,并且在负载峰值、追赶阶段或外部系统(写入到 sink 中)显示临时的减速时,执行流控制的一个重要部分。
  • 某些操作(如大窗口)会导致其下游操作符的负载急剧增加:对于 windows,在构建窗口时,下游操作符可能没有什么要做的,而在发出窗口时却有负载要做。下游并行性的规划需要考虑到窗口发出的数量以及处理这样一个峰值的速度。

重要提示:为了允许以后添加资源,请确保将数据流程序的最大并行度设置为一个合理的数字。最大并行度定义了在重新扩展程序时(通过一个保存点)可以将程序并行度设置到多高。

Flink 内部跟踪并行状态的最大粒度。Flink 的设计致力于使其高效地获得最大并行度的极高值,即使是在执行并行度较低的程序时也是如此。

压缩

Flink 为所有检查点和保存点提供了可选的压缩(默认值:off)。目前,压缩总是使用 snappy 压缩算法(版本1.1.4),计划在未来支持自定义压缩算法。压缩作用于键控状态下键组的粒度,即每个键组可以单独解压缩,这对于扩缩容非常重要。

配置压缩代码样例:

ExecutionConfig executionConfig = new ExecutionConfig();
executionConfig.setUseSnapshotCompression(true);

以上配置对增量快照没有影响,因为它们使用的是 RocksDB 的内部格式,而该格式总是使用 snappy 压缩。

本地恢复任务

触发

在 Flink 的检查点中,每个任务生成其状态的快照,然后将其写入分布式存储。每个任务通过发送一个描述状态在分布式存储中的位置的句柄,向作业管理器确认状态的成功写入。然后,作业管理器从所有任务收集句柄,并将它们绑定到检查点对象中。

在进行恢复时,作业管理器打开最新的检查点对象并将句柄发送回相应的任务,然后这些任务可以从分布式存储中恢复它们的状态。使用分布式存储来存储状态有两个重要的优点。首先,存储是容错的,其次,分布式存储中的所有状态对所有节点都是可访问的,并且可以很容易地进行重新分配(例如,重新缩放)。

然而,使用远程分布式存储也有一个很大的缺点:所有任务都必须通过网络从远程位置读取它们的状态。在许多场景中,恢复可以将失败的任务重新安排到与前一次运行相同的任务管理器中(当然也有例外,比如机器故障),但是我们仍然必须读取远程状态。这可能导致大型状态的恢复时间很长,即使一台机器上只有一个小故障。

捷径

Task-local 状态复苏目标这个问题的恢复时间长,主要观点如下:对于每个检查点,每个任务不仅写任务状态的分布式存储,但也保持中等状态快照副本的存储本地的任务(例如,在本地磁盘或内存)。请注意,快照的主存储仍然必须是分布式存储,因为本地存储不能确保节点故障下的持久性,也不能为其他节点提供访问以重分发状态,所以此功能仍然需要主存储

但是,对于每个可以重新调度到前一个位置进行恢复的任务,我们可以从次要的本地副本恢复状态,从而避免了远程读取状态的开销。由于许多故障不是节点故障,而且节点故障通常一次只影响一个或很少几个节点,因此在恢复过程中,大多数任务很可能返回到它们以前的位置,并发现它们的本地状态完好无损。这就是使局部恢复在减少恢复时间方面有效的原因。

请注意,根据所选的状态后端和检查点策略,对于创建和存储辅助本地状态副本的每个检查点,可能会产生一些额外的成本。例如,在大多数情况下,实现只是将对分布式存储的写入复制到本地文件。

3.4、Flink 集群部署(Deployment & Operations)- State & Fault Tolerance(Flink状态和容错)之 调优检查点和大状态_第2张图片

主(分布式存储)和次(任务-本地)状态快照的关系

限制:目前,任务-本地恢复仅覆盖键控状态后端。键控状态通常是状态中最大的部分。在不久的将来,我们还将介绍操作符状态和计时器。

任务本地状态通常被认为是一个次要副本,检查点状态的基本事实是分布式存储中的主要副本。这对检查点和恢复期间的本地状态问题有影响:

  • 对于检查点,主副本必须是成功的,而生成辅助副本的失败不会使检查点失败。如果不能创建主副本,即使成功创建了辅助副本,检查点也会失败。
  • 作业管理器只确认和管理主副本,任务管理器拥有辅助副本,它们的生命周期可以独立于主副本。例如,可以将3个最新检查点的历史作为主要副本保留,只保留最新检查点的任务本地状态。
  • 对于恢复,如果有匹配的辅助副本可用,则 Flink 总是首先尝试从任务本地状态恢复。如果从辅助副本恢复过程中出现任何问题,Flink 将透明地重试从主副本恢复任务。只有主副本和(可选的)辅助副本失败时,恢复才失败。在这种情况下,根据配置,Flink 仍然可以退回到以前的检查点。
  • 任务本地副本可能只包含完整任务状态的一部分(例如,在编写一个本地文件时发生异常)。在这种情况下,Flink 将首先尝试在本地恢复本地部分,从主副本恢复非本地状态。主状态必须始终是完整的,并且是任务-本地状态的父集。
  • 任务本地状态可以具有与主状态不同的格式,它们不需要字节相同。例如,任务本地状态甚至可能是由堆对象组成的内存中状态,而不是存储在任何文件中。
  • 如果某个任务管理器丢失,则其所有任务的本地状态将丢失。

task-local 配置恢复

任务本地恢复在默认情况下是不激活的,可以通过 Flink 的配置和 state.backend.local-recovery 指定 CheckpointingOptions.LOCAL_RECOVERY 来激活使用。此设置的值可以为 true 以启用,也可以为 false (默认值)以禁用本地恢复。

关于不同状态后端任务-本地恢复的详细信息

以下状态后端可以支持任务-本地恢复。

  • FsStateBackend:关键状态支持任务-本地恢复。实现将把状态复制到一个本地文件。这可能会带来额外的写成本并占用本地磁盘空间。将来,我们可能还会提供一个将任务本地状态保存在内存中的实现。
  • RocksDBStateBackend:关键状态支持任务本地恢复。对于完整的检查点,状态被复制到一个本地文件中。这可能会带来额外的写成本并占用本地磁盘空间。对于增量快照,本地状态基于RocksDB的本地检查点机制。该机制也用作创建主副本的第一步,这意味着在这种情况下,创建辅助副本不需要额外的成本。我们只需保留本机检查点目录,而不是在将其上载到分布式存储后将其删除。这个本地副本可以与RocksDB的工作目录共享活动文件(通过硬链接),因此对于活动文件,也不需要为增量快照的任务本地恢复消耗额外的磁盘空间。使用硬链接还意味着RocksDB目录必须与所有可用于存储本地状态的配置本地恢复目录位于同一物理设备上,否则建立硬链接可能会失败(参见FLINK-10954)。目前,当RocksDB目录被配置为位于多个物理设备上时,这也会阻止使用本地恢复。

配置保存调度

任务本地恢复假设在故障情况下保留分配的任务调度,其工作原理如下。每个任务都会记住它之前的分配,并在恢复时请求重新启动相同的槽位。如果这个插槽不可用,任务将从资源管理器请求一个新的插槽。这样,如果任务管理器不再可用,则无法返回到以前位置的任务将不会将其他正在恢复的任务赶出它们以前的位置。

我们的理由是,前一个插槽只能在任务管理器不再可用时消失,在这种情况下,一些任务必须请求一个新的插槽。在我们的调度策略中,我们为最大数量的任务提供了从它们的本地状态恢复的机会,并避免了任务从另一个任务窃取它们之前的插槽的级联效应。

 

 

原文地址:https://ci.apache.org/projects/flink/flink-docs-release-1.9/ops/state/large_state_tuning.html

你可能感兴趣的:(大数据,Flink,#,Flink,Deployment)