FlinK之检查点与保存点机制

检查点Checkpoint

概述

在 Flink 中,检查点是用于实现状态一致性和故障恢复的关键机制。检查点功能可确保作业的状态在发生故障时能够进行可靠地恢复。

检查点具有以下特性:

状态一致性:检查点会将作业的状态数据保存在持久化存储中,以确保数据的一致性。通过在特定时间点对作业状态进行快照,可以捕获整个作业的状态

容错性:当作业发生故障时,Flink可以使用最近的检查点来恢复作业的状态。恢复作业时,Flink会从最近一个成功完成的检查点开始进行恢复操作

同步机制:为了确保一致性,在触发检查点时,Flink会暂停作业中的所有计算任务,并将状态快照写入持久化存储中。一旦检查点完成,作业的计算任务将会继续执行

检查点模式:Flink提供两种检查点模式:
	Exactly-once:确保数据在一次计算中只会被处理一次,并且状态可以准确地恢复到检查点的状态
	At-least-once:允许数据在一次计算中可能被处理多次,但可以在发生故障后恢复到检查点的状态

高可用性:Flink 支持将检查点数据存储在分布式文件系统(如 HDFS)上,以提供高可用性和容错能力

保存时机

在Flink中,检查点的保存是通过配置检查点间隔和触发条件来控制

检查点间隔 Checkpoint Interval:

可以配置一个时间间隔,在该间隔时间过后,Flink 将自动触发一个新的检查点

检查点间隔可以根据应用程序的需求进行调整,通常是根据数据处理的延迟和吞吐量来决定

较短的检查点间隔意味着更频繁的检查点,但可能会增加系统开销和延迟;而较长的间隔则会减少开销,但会增加数据丢失的潜在风险

触发条件 Triggering Condition:

精确一次处理 Exactly-Once Processing:在 Flink 中,可以通过配置“精确一次处理”的语义来触发检查点。这意味着只有在前一个检查点成功完成后才会触发新的检查点,确保数据的一致性和完整性。

基于数据量 Data Size:可以设置一个数据大小阈值,在接收到一定数量的记录后触发检查点。这个阈值可以根据应用程序的需求进行调整,以平衡开销和一致性。

基于时间 Time Based:可以设置一个时间阈值,在一定时间间隔内没有触发检查点时,强制触发一个检查点。这用于确保即使在没有活动数据的情况下也能定期保存状态数据。

保存与恢复

Flink 中检查点的保存过程大致分为以下几个步骤:

1.触发检查点:Flink会根据配置的触发条件(例如时间间隔或数据量)自动触发一个新的检查点。或者可以通过编程方式在代码中显式触发检查点。

2.暂停计算任务:在触发检查点时,Flink会暂停应用程序的所有计算任务,确保没有新的状态更新。

3.状态快照:一旦计算任务暂停,Flink将应用程序的状态数据进行快照(snapshot),包括Keyed State和Operator State。这些状态数据会被写入到持久化存储中。

4.存储检查点数据:Flink将状态快照持久化存储到配置的检查点存储后端中。具体存储的方式取决于所选择的存储后端,例如写入到分布式文件系统、对象存储服务或远程文件系统。

5.恢复计算任务:一旦状态数据持久化完成,Flink 将恢复应用程序的计算任务,继续处理输入数据。

在发生故障时,Flink可以使用已保存的检查点数据进行应用程序的恢复,过程如下:

1.检测到故障:Flink监控作业状态时会检测到故障,例如计算节点崩溃。

2.选择最近的检查点:一旦故障被检测到,Flink会选择最近成功完成的检查点。

3.从检查点恢复状态:Flink会从所选检查点的存储位置中读取状态数据,并将其加载到应用程序中,恢复应用程序的状态。

4.恢复计算任务:一旦状态数据加载完成,Flink 将恢复应用程序的计算任务,从故障发生时的状态继续处理输入数据。

检查点算法

在Flink中,采用了基于Chandy-Lamport算法的分布式快照,可以在不暂停整体流处理的前提下,将状态备份保存到检查点。

Flink的检查点算法是基于快照的机制实现的,它是一种增量快照算法,称为异步屏障(asynchronous barrier)算法,用于实现检查点的一致性。

通过异步屏障算法,Flink可以实现在分布式环境中对应用程序状态的一致性保存,确保在故障发生时能够正确地恢复状态,并继续进行数据处理。这种算法的设计考虑了分布式环境下的并发和异步处理的特点,以保证高效且可靠的检查点机制。

检查点算法的基本流程:

触发检查点:根据配置的触发条件,Flink触发一个新的检查点

异步屏障插入:在所有算子之间插入屏障(barrier)操作。屏障是一种特殊的记录,在流中传播,并且在到达算子后会阻塞输入,直到在所有流并行分区的所有算子上都收到屏障为止

等待所有屏障:一旦插入屏障,所有算子将等待所有输入流上的屏障。这可以确保在屏障之后的所有记录都是一致的,并且有效防止任何后续状态更新

状态快照:在等待所有屏障后,Flink将进行实际的状态快照操作。它会在算子中将状态数据写入到持久化存储中,以进行检查点保存

完成屏障传播:在状态快照完成后,屏障将被传播到下游算子,通知它们可以继续处理记录

恢复计算任务:在发生故障时,Flink使用已保存的检查点数据进行应用程序的恢复,通过加载状态数据来恢复应用程序的状态

检查点分界线

检查点分界线(Barrier)是在Flink中用于保证检查点的一致性的特殊记录。在Flink的异步屏障算法中,屏障插入和分界线的概念是相关的。

当触发一个新的检查点时,Flink 在流中的所有算子之间插入屏障操作。屏障是一种特殊的记录,会在流中传播,并且在到达算子后会阻塞输入,直到在所有流并行分区的所有算子上都收到屏障为止。

屏障的到达具有两个重要的作用:

一致性分界线:屏障的到达表示在该位置之前的所有记录都被视为属于之前的检查点,而之后的记录将属于新检查点。这样,屏障将流分割为多个一致性分段,确保在一个检查点完成后,新的检查点将在屏障之后开始。
	
阻塞输入:屏障的到达会阻塞输入,直到所有输入流并行分区上的算子都收到屏障为止。这确保了在算子处理记录之前,所有输入数据都已经达到一致的检查点位置。

通过插入屏障和处理屏障的到达,Flink 可以保证检查点的一致性和状态快照的完整性。屏障作为检查点的分界线,将流划分为单个一致性分段,并确保所有算子在检查点完成后开始处理新的数据记录。

Barrier对齐的精准一次

分布式快照算法之精确一次(Exactly-once)语义,它结合了屏障对齐(Barrier Alignment)和状态快照(State Snapshotting)来实现精确一次的语义保证。

过程如下:

屏障对齐:在启动检查点时,Flink 将在流的所有并行分区之间插入屏障(barrier)。屏障的到达通知所有算子在其上的输入都已经到达一致的检查点位置。

状态快照:在屏障对齐后,Flink 使用状态快照机制将算子的状态数据保存到持久化存储中,形成一个一致的检查点。

检查点确认:在状态快照完成后,Flink 会等待所有参与分布式快照的任务确认其已完成检查点。这可以确保所有任务已经成功保存了状态数据。

任务协调器通知:一旦所有任务都确认完成检查点,任务协调器将发送一个通知,表示检查点已经完成。

恢复:当发生故障时,Flink 可以使用已保存的检查点数据进行应用程序的恢复。它将加载检查点中的状态数据,并通过屏障对齐来确保在故障点之后的记录不会被处理。

该算法有效地保证了在故障发生时可以正确地恢复应用程序的状态,并避免了重复处理数据或丢失数据的情况,从而实现了精确一次的语义保证。

一个Task收到所有上游同一个编号的barrier之后,才会对自己的本地状态做备份,在barrier对齐过程中,barrier后面的数据阻塞等待,不会越过barrier

Barrier对齐的至少一次

分布式快照算法之至少一次(At-least-once)语义,它是通过基于异步屏障(Asynchronous Barrier)的检查点算法来实现的。

过程如下:

触发检查点:根据配置的触发条件,Flink 触发一个新的检查点。

异步屏障插入:在所有算子之间插入异步屏障(barrier)操作。屏障是特殊记录,在流中传播,并且在到达算子后会阻塞输入,直到在所有流并行分区的所有算子上都收到屏障为止。

检查点确认:在屏障插入之后,Flink 会等待所有参与检查点的任务确认它们已经处理了该屏障。一旦收到所有任务的确认,就可以继续进行下一步。

状态快照:在检查点确认之后,Flink 执行实际的状态快照操作。它会在算子中将状态数据写入持久化存储以进行检查点保存。

完成屏障传播:在状态快照完成后,屏障将被传播到下游算子,通知它们可以继续处理记录。

该算法尽管在故障发生时可能会出现一些重复处理的情况,但它确保了至少一次的语义,即每个记录在最多一次被处理。这是一种容错机制,可以保证在发生故障时能够正确地恢复应用程序的状态,确保数据被至少一次地处理。

一个Task收到所有上游同一个编号的barrier之后,才会对自己的本地状态做备份,在barrier对齐过程中,先到的barrier,其后面的数据不阻塞接着计算

非Barrier对齐的精准一次

分布式快照算法之非Barrier对齐的精确一次语义,它使用了异步处理与检查点保存的方式来实现精确一次的语义。

过程如下:

触发检查点:根据配置的触发条件,Flink 触发一个新的检查点。

非Barrier对齐:不像Barrier对齐算法那样在流中插入屏障,非Barrier对齐算法利用异步处理和状态保存来实现。在每个算子中,Flink 会使用异步的方式执行状态快照,并将状态数据保存到持久化存储中,形成一个一致的检查点。

检查点确认:一旦算子完成了状态快照,Flink 会等待所有参与检查点的任务确认它们已经完成了状态保存。这可以确保所有任务都成功保存了状态数据。

任务协调器通知:当所有任务确认已完成检查点后,任务协调器将发送一个通知,表示检查点已经完成。

恢复:当发生故障时,Flink 可以使用已保存的检查点数据进行应用程序的恢复。它会从持久化存储中加载检查点中的状态数据,并通过异步处理来确保在故障点之后的记录不会被重复处理。

该算法利用异步处理和检查点保存来实现状态一致性,并通过恢复机制来处理故障情况,从而确保应用程序的状态能够正确地恢复。

一个Task收到第一个barrier时,就开始执行备份,能保证精准一次

先到的barrier,将本地状态备份,其后面的数据接着计算输出

未到的barrier,其前面的数据接着计算输出,同时也保存到备份中

最后一个barrier到达该Task时,这个Task的备份结束

检查点配置

检查点的作用是为了故障恢复,不能因为保存检查点占据了大量时间、导致数据处理性能明显降低。为了兼顾容错性和处理性能,可以在代码中对检查点进行各种配置。

启用检查点

Flink程序是默认禁用检查点的,为Flink应用开启自动保存快照的功能,需要在代码中显式地调用执行环境的enableCheckpointing()方法

StreamExecutionEnvironment env =StreamExecutionEnvironment.getExecutionEnvironment();
	// 状态检查点间隔,单位为毫秒,表示周期性保存检查点的间隔时间。默认间隔周期500毫秒,已经被弃用。
	// 每隔1秒启动一次检查点保存
	env.enableCheckpointing(1000);
	// 启用检查点: 默认是barrier对齐的,周期为5s, 精准一次
    env.enableCheckpointing(5000, CheckpointingMode.EXACTLY_ONCE);
    // 周期为5s, 至少一次
    env.enableCheckpointing(5000, CheckpointingMode.AT_LEAST_ONCE);

检查点的间隔时间是对处理性能和故障恢复速度的一个权衡。

如果希望对性能的影响更小,可以调大间隔时间

如果希望故障重启后迅速赶上实时的数据处理,就需要将间隔时间设小一些

指定存储位置

检查点具体的持久化存储位置,取决于检查点存储的设置。默认情况下,检查点存储在JobManager的堆内存中。而对于大状态的持久化保存,Flink也提供了在其他存储位置进行保存的接口。

Flink持久化存储位置主要提供了两种:作业管理器的堆内存文件系统。通过调用检查点配置的setCheckpointStorage()来配置,传入一个CheckpointStorage的实现类。

1.配置存储检查点到JobManager堆内存

StreamExecutionEnvironment env =StreamExecutionEnvironment.getExecutionEnvironment();
CheckpointConfig checkpointConfig = env.getCheckpointConfig();
checkpointConfig.setCheckpointStorage(new JobManagerCheckpointStorage());

2.配置存储检查点到文件系统 实际生产应用一般会将CheckpointStorage配置为高可用的分布式文件系统

StreamExecutionEnvironment env =StreamExecutionEnvironment.getExecutionEnvironment();
CheckpointConfig checkpointConfig = env.getCheckpointConfig();
checkpointConfig .setCheckpointStorage(new FileSystemCheckpointStorage("hdfs://node01:9000/flink/checkpoints"));

其它配置

检查点还有很多可以配置的选项,可以通过获取检查点配置来进行设置。

CheckpointConfig checkpointConfig = env.getCheckpointConfig();
		// 超时时间: 默认10分钟。用于指定检查点保存的超时时间,超时没完成就会被丢弃掉。传入一个长整型毫秒数作为参数,表示超时时间
        checkpointConfig.setCheckpointTimeout(60000);

        // 最大并发检查点数量:用于指定运行中的检查点最多可以有多少个
        // 由于每个任务的处理进度不同,完全可能出现后面的任务还没完成前一个检查点的保存、前面任务已经开始保存下一个检查点。这个参数就是限制同时进行的最大数量
        checkpointConfig.setMaxConcurrentCheckpoints(1);

        // 最小间隔时间: 用于指定在上一个检查点完成之后,检查点协调器最快等多久可以出发保存下一个检查点的指令。当指定这个参数时,实际并发为1
        // 直白说就是:上一轮checkpoint结束 到 下一轮checkpoint开始之间的间隔
        checkpointConfig.setMinPauseBetweenCheckpoints(1000);


        // 开启外部持久化存储: 用于开启检查点的外部持久化,而且默认在作业失败的时候不会自动清理
        // 传入的参数ExternalizedCheckpointCleanup指定了当作业取消的时候外部的检查点该如何清理,即数据是否保留在外部系统
        // DELETE_ON_CANCELLATION: 在作业取消的时候会自动删除外部检查点,但是如果是作业失败退出,则会保留检查点
        // RETAIN_ON_CANCELLATION: 作业取消的时候也会保留外部检查点
        checkpointConfig.setExternalizedCheckpointCleanup(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);

        // 检查点连续失败次数:用于指定检查点连续失败的次数,当达到这个次数,作业就失败退出。默认为0
        checkpointConfig.setTolerableCheckpointFailureNumber(10);

        // 开启非对齐检查点: 不再执行检查点的分界线对齐操作,启用之后可以大大减少产生背压时的检查点保存时间
        // 设置要求检查点模式必须为精准一次exctly-once,并且最大并发的检查点个数为1
        checkpointConfig.enableUnalignedCheckpoints();

        //  对齐检查点超时时间: 参数只有在启用非对齐检查点的时候有效
        //  默认是0,表示一开始就直接用非对齐检查点。如果设置大于0,一开始会使用对齐的检查点
        //  当对齐时间超过该参数设定的时间,则会自动切换成非对齐检查点
        checkpointConfig.setAlignedCheckpointTimeout(Duration.ofSeconds(1));

通用增量

Flink的增量检查点是一种优化技术,用于减少检查点所需的存储和处理成本。传统的全量检查点需要在每个检查点时保存整个应用程序的状态。而增量检查点仅保存自上一个检查点以来更改的状态部分,从而减少了存储和处理的负担。

通用增量检查点是 Flink1.11版本引入的一项功能。它通过结全量检查点(Full Checkpoint)和增量检查点(Incremental Checkpoint)类型的检查点来实现增量检查点

具体的增量检查点流程如下:

全量检查点:在首次触发检查点时,会执行全量检查点,将整个应用程序的状态保存为检查点。

增量检查点:自上一个检查点以来,当应用程序状态发生更改时,Flink 会执行增量检查点,仅保存这些更改的状态部分。这些更改的状态会与上一个全量检查点的状态进行比较,只保留与之不同的部分。

检查点结果:最终的检查点结果包含上一个全量检查点的状态和增量检查点中的更改部分。

对于故障恢复:

Flink 可以使用这些检查点结果进行恢复操作。首先,将上一个全量检查点的状态加载到内存中,然后依次应用增量检查点的更改部分,即可恢复应用程序的状态。

优缺点:

增量检查点可以减少存储和传输的数据量,从而降低了检查点产生和恢复的开销。然而,由于增量检查点需要进行状态更改的计算和处理,所以会引入一些额外的计算开销

注意:Flink1.15之前,只有RocksDB支持增量快照,开启方式:

EmbeddedRocksDBStateBackend backend = new EmbeddedRocksDBStateBackend(true);

方式一:配置文件指定

# 启用增量检查点
state.backend.changelog.enabled: true
state.backend.changelog.storage: filesystem

# 存储changelog数据
dstl.dfs.base-path: hdfs://node01:9000/changelog
execution.checkpointing.max-concurrent-checkpoints: 1
execution.savepoint-restore-mode: CLAIM

方式二:在代码中设置
需要引入依赖

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-statebackend-changelog</artifactId>
    <version>${
    
    flink.version}</version>
    <scope>runtime</scope>
</dependency>

开启changelog

env.enableChangelogStateBackend(true);

保存点Savepoint

概述

在Flink中,保存点(Savepoints)是一种用于长期存储应用程序状态的机制。保存点可以用于在应用程序遇到故障或需要更改应用程序代码时执行重启和升级。

保存点是一个检查点的特殊形式,它可以用于在不同的 Flink 版本之间迁移作业状态。保存点是跨版本的状态快照,允许将作业从一个版本的 Flink 迁移到另一个版本而不丢失状态。保存点通常用于进行作业升级、实验和版本回退。

保存点与检查点区别:

保存点与检查点最大的区别就是触发的时机:

检查点是由Flink自动管理的,定期创建,发生故障之后自动读取进行恢复

保存点不会自动创建,必须由用户明确地手动触发保存操作。

保存点的用途:

保存点可以当作一个强大的运维工具来使用。可以在需要的时候创建一个保存点,然后停止应用,做一些处理调整之后再从保存点重启。

应用场景:

更新应用程序

作业升级

调整并行度

实验和测试

版本回退

暂停应用程序

故障排查和分析

算子ID:

算子ID可以在代码中直接调用。对于没有设置ID的算子,Flink默认会自动进行设置,在重新启动应用后可能会导致ID不同而无法兼容以前的状态。

在程序中为每一个算子手动指定ID

DataStream<String> stream = env
    .addSource(new StatefulSource()).uid("source-id")
    .map(new StatefulMapper()).uid("mapper-id")
    .print();

使用保存点

1.创建保存点

在命令行中为运行的作业创建一个保存点镜像

jobId:需要填充要做镜像保存的作业ID

targetDirectory:目标路径可选,表示保存点存储的路径

示例如下:

bin/flink savepoint :jobId [:targetDirectory]

对运行的作业创建保存点,在停掉一个作业时直接创建保存点

bin/flink stop --savepointPath [:targetDirectory] :jobId

2.保存点路径

1.可以通过配置文件flink-conf.yaml来修改保存点的默认路径

state.savepoints.dir: hdfs://node01:9000/flink/savepoints

2.对于单独的作业可以在程序代码中通过执行环境来设置

env.setDefaultSavepointDir("hdfs://node01:9000/flink/savepoints");

3.从保存点重启应用

-s参数:指定保存点的路径

runArgs:其它启动时的参数
bin/flink run -s :savepointPath [:runArgs]

切换状态后端

使用savepoint恢复状态的时候,可以更换状态后端。建议不要在代码中指定状态后端, 通过配置文件来配置或者-D参数配置。

1.提交flink作业

bin/flink run-application -d -t yarn-application -Dstate.backend=hashmap -c cn.ybzy.demo.SavepointDemo FlinkDemo.jar

2.停止flink作业时,触发保存点

方式一:stop优雅停止并触发保存点,要求source实现StoppableFunction接口

bin/flink stop -p savepoint路径 job-id -yid application-id

3.从savepoint恢复作业,同时修改状态后端

bin/flink run-application -d -t yarn-application -s hdfs://node01:9000/flink/savepoint-26c8e0-9e0b6cd976a4 -Dstate.backend=rocksdb -c cn.ybzy.demo.SavepointDemo FlinkDemo.jar   

4.从保存下来的checkpoint恢复作业

bin/flink run-application -d -t yarn-application -Dstate.backend=rocksdb -s hdfs://node01:9000/flink/4d8435f6be2a1d9e0b6cd976a24f6c8e/chk-175 -c cn.ybzy.demo.SavepointDemo ./FlinkDemo.jar

SQL客户端中操作

提交作业

提交一个insert作业,可以给作业设置名称

INSERT INTO tb_test select  * from datagen;

查看job列表

SHOW JOBS;

触发

设置检查点、保存点路径

SET state.checkpoints.dir='hdfs://node01:9000/ck';
SET state.savepoints.dir='hdfs://node01:9000/sp';

停止作业,触发savepoint

STOP JOB '4d8435f6be2a1d9e0b6cd976a24f6c8e' WITH SAVEPOINT;

恢复

设置恢复的路径,然后提交sql,就会从savepoint恢复

SET execution.savepoint.path='hdfs://node01:9000/sp/savepoint-26c8e0-9e0b6cd976a4';  

--允许跳过无法还原的保存点状态
set 'execution.savepoint.ignore-unclaimed-state' = 'true'; 

跳过无法还原的保存点状态

set 'execution.savepoint.ignore-unclaimed-state' = 'true'; 

使用RESET命令重置execution.savepoint.path配置,其指定后会影响后面执行的所有DML语句

RESET execution.savepoint.path;

猜你喜欢

转载自blog.csdn.net/qq_38628046/article/details/109278175