Overview
为了使Flink应用程序能够大规模可靠地运行,必须满足两个条件:
应用程序需要能够可靠地接受检查点
故障后,资源必须足够赶上输入数据流
第一部分讨论了如何获得性能良好的检查点。 最后一部分介绍了有关计划使用多少资源的一些最佳做法。
1 监视状态和检查点
监视检查点行为的最简单方法是通过用户界面的检查点部分。检查点监视的文档显示了如何访问可用的检查点指标。
扩大检查点时特别感兴趣的两个数字是:
(1)直到操作员开始其检查点的时间:该时间当前未直接暴露,但对应于:checkpoint_start_delay = end_to_end_duration-同步时间-异步时间
(2)当触发检查点的时间一直很长时,这意味着检查点障碍物需要很长时间才能从源头到达操作员。这通常表示系统在恒定的背压下运行。
对齐期间缓冲的数据量。对于一次语义,Flink在接收多个输入流的运算符处对齐流,为该对齐缓冲一些数据。理想情况下,缓冲的数据量较低-较高的数据量意味着在不同时间从不同的输入流接收到
检查点屏障。请注意,当存在瞬时背压,数据偏斜或网络问题时,此处指示的数字有时可能会很高。但是,如果数字一直很高,则意味着Flink将许多资源用于检查点。
2 调整检查点
检查点会定期触发,应用程序可以对其进行配置。如果检查点完成所需的时间长于检查点间隔,则在进行中的检查点完成之前不会触发下一个检查点。默认情况下,正在进行的检查点完成后,将立
即触发下一个检查点。当检查点结束时经常花费比基本间隔更长的时间(例如,因为状态变得比计划的大,或者因为存储检查点的存储暂时变慢),系统会不断获取检查点(一旦完成,新的检查点
将立即启动) 。这可能意味着太多的资源经常被束缚在检查点上,而操作员的进展却很少。此行为对使用异步检查点状态的流应用程序影响较小,但仍可能对整体应用程序性能产生影响。为了防止
这种情况,应用程序可以定义检查点之间的最小持续时间:
StreamExecutionEnvironment.getCheckpointConfig().setMinPauseBetweenCheckpoints(毫秒)
此持续时间是最后一个检查点的结束与下一个检查点的开始之间必须经过的最短时间间隔。下图说明了这如何影响检查点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X70MRwZk-1578476990608)(D:\大数据\Flink大状态与checkpoint\picture\2019-12-24_094624.png)]
注意:可以将应用程序配置为(通过CheckpointConfig)以允许同时运行多个检查点。 对于Flink中具有大状态的应用程序,这通常将过多的资源占用到检查点中。 手动触发保存点时,可能与正在进行的检查点同时进行。
3 调整网络缓冲区
在Flink 1.3之前,增加的网络缓冲区数量也导致增加了检查点时间,因为保持更多的飞行中数据意味着检查点障碍被延迟了。 从Flink 1.3开始,每个传出/传入通道使用的网络缓冲区的数量受到限
制,因此可以在不影响检查点时间的情况下配置网络缓冲区(具体详细内容后续补全)。
4 异步检查点
当状态被异步快照时,检查点的扩展比状态被同步快照时更好。 尤其是在具有多个联接,功能或窗口的更复杂的流应用程序中,这可能会产生深远的影响。为了使状态异步快照,您需要使用支持异
步快照的状态后端。 从Flink 1.3开始,基于RocksDB和基于堆的状态后端(文件系统)都支持异步快照,并且默认情况下使用它。 这既适用于托管操作员状态,也适用于托管键控状态(包括计时器
状态)。注意RocksDB状态后端与基于堆的计时器的组合目前不支持计时器状态的异步快照。 其他状态(如键控状态)仍会异步快照。 请注意,这不是以前版本的回归,将通过FLINK-10026解
决。
5 调整RocksDB
RocksDB状态后端是许多大型Flink流应用程序的状态存储主力。 后端的扩展范围远远超出了主内存,并且可以可靠地存储较大的键控状态。不幸的是,RocksDB的性能可能会因配置而异,并且很
少有文档介绍如何正确调整RocksDB。 例如,默认配置是针对SSD定制的,并且在旋转磁盘上执行次优的配置。
5.1增量检查点
与完整检查点相比,增量检查点可以显着减少检查点时间,但要花费(可能)更长的恢复时间。 核心思想是,增量检查点仅记录对先前完成的检查点的所有更改,而不生成状态后端的完整,自包含
的备份。 这样,增量检查点就建立在先前的检查点上。 Flink利用RocksDB的内部备份机制,这种机制可以随着时间的推移自我整合。 结果,Flink中的增量检查点历史记录不会无限期增长,并且最
终会自动包含并清除旧的检查点。尽管我们强烈建议对大状态使用增量检查点,但是请注意,这是一项新功能,默认情况下当前未启用。 要启用此功能,用户可以实例化RocksDBStateBackend,
并将构造函数中的相应布尔标志设置为true,例如:
RocksDBStateBackend backend = new RocksDBStateBackend(filebackend, true);
5.2 RocksDB计时器
对于RocksDB,用户可以选择将计时器存储在堆中(默认)还是在RocksDB内部。 基于堆的计时器对于较少数量的计时器可以具有更好的性能,而在RocksDB中存储计时器可以提供更高的可伸缩
性,因为RocksDB中的计时器数量可以超过可用的主内存(向磁盘溢出)。将RockDB用作状态后端时,可以通过Flink的配置通过选项键state.backend.rocksdb.timer-service.factory选择计时器存储
的类型。 可能的选择是堆(默认情况下将计时器存储在堆上)和rocksdb(将计时器存储在RocksDB中)。
注意:RocksDB状态后端与基于堆的计时器的组合目前不支持计时器状态的异步快照。 其他状态(如键控状态)仍会异步快照。 请注意,这不是以前版本的回归,将通过FLINK-10026解决。
5.3 预定义选项
Flink针对不同的设置为RocksDB提供了一些预定义的选项集合,并且存在两种将这些预定义的选项传递给RocksDB的方法:
通过选项键state.backend.rocksdb.predefined-options通过flink-conf.yaml配置预定义选项。 此选项的默认值为DEFAULT,这意味着PredefinedOptions.DEFAULT。
以编程方式设置预定义选项,例如 RocksDBStateBackend.setPredefinedOptions(PredefinedOptions.SPINNING_DISK_OPTIMIZED_HIGH_MEM)。
我们希望随着时间的推移会积累更多这样的配置文件。 当您发现一组效果很好并且似乎可以代表某些工作负载的选项时,请随意提供此类预定义的选项配置文件。注意以编程方式设置的预定义选项
将覆盖通过flink-conf.yaml配置的选项。
5.4将选项工厂传递给RocksDB
在Flink中,有两种方法可以将选项工厂传递给RocksDB:
(1)通过flink-conf.yaml配置工厂选项。 您可以通过选项键state.backend.rocksdb.options-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.yaml配置的选项工厂,并且如果进行了配置或设置,则选项工厂具有比预定义选项更高的优先级。
注意:RocksDB是一个本机库,直接从进程而不是从JVM分配内存。 通常必须通过将TaskManager的JVM堆大小减少相同的数量来解决分配给RocksDB的所有内存。 不这样做可能导致YARN /
Mesos / etc终止JVM进程以分配比配置更多的内存。
6 容量规划
本节讨论如何确定应使用多少资源才能使Flink作业可靠地运行。容量规划的基本经验法则是:
(1)正常运行应具有足够的容量,以使其不能在恒定的背压下运行。有关如何检查应用程序是否在背压下运行的详细信息,(请参见背压监视)。
(2)在无故障时间内无程序运行无背压所需的资源的基础上,提供一些额外的资源。需要这些资源来“追赶”在应用程序恢复期间累积的输入数据。应该有多少取决于恢复操作通常需要多长时间
(取决于故障转移时需要加载到新TaskManager中的状态的大小)以及该方案要求故障恢复的速度。
重要说明:应该在激活检查点的情况下建立基线,因为检查点会占用一些资源(例如网络带宽)。
(3)暂时的背压通常是可以的,并且在负载峰值,追赶阶段或外部系统(写入接收器中)表现出暂时的减速期间,是执行流控制的重要部分。
(4)某些操作(如大窗口)会给其下游操作员带来不菲的负担:就窗口而言,在构建窗口时,下游操作员可能无事可做,而在发射窗口时则有负担。下游并行性的规划需要考虑到窗口发射多少光
以及这种尖峰需要处理多快。
注意:为了允许以后添加资源,请确保将数据流程序的最大并行度设置为合理的数量。最大并行度定义了在重新缩放程序时(通过保存点)可以将程序并行度设置为多高。
Flink的内部簿记功能以max-parallelism-很多键组的粒度跟踪并行状态。 Flink的设计力争使最大并行度具有很高的价值,即使执行低并行度的程序也是如此。
7 Compression 压缩
Flink为所有检查点和保存点提供了可选的压缩(默认值:关闭)。 当前,压缩始终使用灵活的压缩算法(版本1.1.4),但是我们计划在将来支持自定义压缩算法。 压缩是在处于键控状态的键组的
粒度上进行的,即每个键组可以分别解压缩,这对于重新缩放很重要。可以通过ExecutionConfig激活压缩:
ExecutionConfig executionConfig = new ExecutionConfig();
executionConfig.setUseSnapshotCompression(true);
注意压缩选项对增量快照没有影响,因为它们使用的是RocksDB的内部格式,该格式始终使用开箱即用的快速压缩。
8 任务本地恢复
8.1 Motivation 动机
在Flink的检查点中,每个任务都会生成其状态的快照,然后将其写入分布式存储。每个任务通过发送一个描述状态在分布式存储中的位置的句柄,将状态成功写入作业管理器。反过来,作业管理器
从所有任务中收集句柄并将它们捆绑到检查点对象中。在恢复的情况下,作业管理器将打开最新的检查点对象,并将句柄发送回相应的任务,然后这些任务可以从分布式存储中恢复其状态。使用分
布式存储来存储状态有两个重要优点。首先,该存储是容错的,其次,分布式存储中的所有状态对于所有节点都是可访问的,并且可以很容易地重新分配(例如,用于重新缩放)。但是,使用远程
分布式存储也有一个很大的缺点:所有任务都必须通过网络从远程位置读取其状态。在许多情况下,恢复可以将失败的任务重新安排到与上次运行相同的任务管理器中(当然,还有诸如机器故障之
类的异常),但是我们仍然必须读取远程状态。即使在单个计算机上只有很小的故障,这也可能导致大型状态的恢复时间较长。
8.2 Approach 方法
任务本地状态恢复正好解决了恢复时间长的问题,其主要思想如下:对于每个检查点,每个任务不仅将任务状态写入分布式存储,而且还将状态快照的辅助副本保存在磁盘中。任务本地的存储(例
如,本地磁盘或内存中)。请注意,快照的主要存储区仍必须是分布式存储区,因为本地存储区不能确保在节点故障下的持久性,并且也不能为其他节点提供重新分配状态的访问权限,因此此功能
仍需要主要副本。但是,对于可以重新安排到先前位置进行恢复的每个任务,我们可以从辅助本地副本还原状态,并避免了远程读取状态的开销。鉴于许多故障不是节点故障,并且节点故障通常一
次只影响一个或很少几个节点,因此很有可能大多数任务可以在恢复中返回其先前位置并找到其完整状态。这就是使本地恢复有效地减少恢复时间的原因。请注意,这取决于创建和存储辅助本地状
态副本的每个检查点的一些额外费用,具体取决于所选的状态后端和检查点策略。例如,在大多数情况下,实现将简单地将对分布式存储的写入复制到本地文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twK2F97M-1578476990610)(D:\大数据\Flink大状态与checkpoint\picture\2019-12-24_102402.png)]
8.3主(分布式存储)和辅助(任务本地)状态快照的关系
任务本地状态始终被视为辅助副本,检查点状态的基础是分布式存储中的主要副本。这对检查点和恢复期间的本地状态问题具有影响:
(1)对于检查点,主要副本必须成功,并且生成次要本地副本的失败不会使检查点失败。如果无法创建主副本,则即使成功创建了辅助副本,检查点也会失败。
(2)作业经理仅确认和管理主要副本,任务管理者拥有次要副本,并且其生命周期可以独立于其主要副本。例如,可以将3个最新检查点的历史记录保留为主副本,而仅保留最新检查点的任务本
地状态。
(3)为了进行恢复,如果有匹配的辅助副本可用,Flink将始终首先尝试从任务本地状态还原。如果从辅助副本恢复过程中发生任何问题,Flink将透明地重试以从主副本恢复任务。仅当主副本和
(可选)辅助副本失败时,恢复才会失败。在这种情况下,根据配置的不同,Flink仍可能退回到较早的检查点。
(4)任务本地副本有可能仅包含完整任务状态的一部分(例如,写入一个本地文件时发生异常)。在这种情况下,Flink将首先尝试在本地恢复本地部分,从主副本还原非本地状态。主状态必须始
终是完整的,并且是任务本地状态的超集。
(5)任务本地状态可以具有与主状态不同的格式,不需要字节相同。例如,任务本地状态甚至可能是由堆对象组成的内存中状态,而不是存储在任何文件中。
(6)如果任务管理器丢失,则其所有任务的本地状态也将丢失。
8.4配置任务本地恢复
默认情况下,任务本地恢复是停用的,可以通过Flink的配置(通过CheckpointingOptions.LOCAL_RECOVERY中指定的键state.backend.local-recovery激活)来激活。 此设置的值可以为true以启用,也可以为false(默认)以禁用本地恢复。
8.5 有关不同状态后端的任务本地恢复的详细信息
局限性:当前,任务本地恢复仅涵盖键控状态后端。到目前为止,键控状态通常是该状态的最大部分。在不久的将来,我们还将介绍操作员的状态和计时器。
以下状态后端可以支持任务本地恢复。
(1)FsStateBackend:密钥状态支持任务本地恢复。该实现会将状态复制到本地文件。这会导致额外的写入成本并占用本地磁盘空间。将来,我们可能还会提供将任务本地状态保存在内存中的
实现。
(2)RocksDBStateBackend:密钥状态支持任务本地恢复。对于完整的检查点,状态被复制到本地文件。这会导致额外的写入成本并占用本地磁盘空间。对于增量快照,本地状态基于RocksDB
的本机检查点机制。此机制也用作创建主副本的第一步,这意味着在这种情况下,创建第二副本不会引入额外的成本。我们只需保留本地检查点目录,而不是在上载到分布式存储后将其删除。此本
地副本可以与RocksDB的工作目录共享活动文件(通过硬链接),因此对于活动文件,使用增量快照进行任务本地恢复也不会占用额外的磁盘空间。使用硬链接还意味着RocksDB目录必须与所有可
用于存储本地状态的配置本地恢复目录位于同一物理设备上,否则建立硬链接可能会失败(请参阅FLINK-10954)。当前,当RocksDB目录配置为位于多个物理设备上时,这还阻止使用本地恢复。
8.6 保留分配的调度
任务本地恢复假定发生故障时保留分配的任务调度,其工作方式如下。 每个任务都会记住其先前的分配,并请求完全相同的插槽以重新启动以进行恢复。 如果此插槽不可用,则任务将向资源管理器
请求一个新的新插槽。 这样,如果任务管理器不再可用,那么无法返回到其先前位置的任务将不会将其他正在恢复的任务从先前的插槽中移出。 我们的理由是,只有当任务管理器不再可用时,先前
的插槽才能消失,在这种情况下,某些任务无论如何都必须请求一个新的插槽。 通过我们的调度策略,我们为最大数量的任务提供了从其本地状态恢复的机会,并避免了任务相互级联窃取以前的任
务的级联效应。