checkpoint是Flink容错的核心机制。它可以定期地将各个Operator处理的数据进行快照存储( Snapshot )。如果Flink程序出现宕机,可以重新从这些快照中恢复数据。
一、一致性
在流处理中,一致性可以分为3个级别:
at-most-once(最多一次): 这其实是没有正确性保障的委婉说法——故障发生之后,计数结果可能丢失。
at-least-once(至少一次): 这表示计数结果可能大于正确值,但绝不会小于正确值。也就是说,计数程序在发生故障后可能多算,但是绝不会少算。
exactly-once(严格一次): 这指的是系统保证在发生故障后得到的计数结果与正确值一致.既不多算也不少算。
at-least-once:第一代流处理器(如Storm和Samza)刚问世时只保证at-least-once,保证exactly-once的系统实现起来更复杂。这在基础架构层(决定什么代表正确,以及exactly-once的范围是什么)和实现层都很有挑战性 流处理系统的早期用户愿意接受框架的局限性,并在应用层想办法弥补(例如使应用程序具有幂等性,或者用批量计算层再做一遍计算)。
端到端的状态一致性
具体划分如下:
source端
需要外部源可重设数据的读取位置.目前我们使用的Kafka Source具有这种特性: 读取数据的时候可以指定offset
flink内部
依赖checkpoint机制
sink端
需要保证从故障恢复时,数据不会重复写入外部系统.
有2种实现形式:
a)幂等(Idempotent)写入 所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改,也就是说,后面再重复执行就不起作用了。
b)事务性(Transactional)写入
二、Checkpoint原理
"检查点"(checkpoint)的特性,在出现故障时将系统重置回正确状态。
checkpoint机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保证应用流图状态的一致性.
快照的实现算法:
a)简单算法–暂停应用, 然后开始做检查点, 再重新恢复应用
b)Flink的改进Checkpoint算法. Flink的checkpoint机制原理来自"Chandy-Lamport algorithm"算法(分布式快照算)的一种变体: 异步 barrier 快照(asynchronous barrier snapshotting)
每个需要checkpoint的应用在启动时,Flink的JobManager为其创建一个CheckpointCoordinator,CheckpointCoordinator全权负责本应用的快照制作。
Barrier
流的barrier是Flink的Checkpoint中的一个核心概念. 多个barrier被插入到数据流中, 然后作为数据流的一部分随着数据流动(有点类似于Watermark).这些barrier不会跨越流中的数据.
Flink 分布式快照里面的一个核心的元素就是流屏障(stream barrier)。这些屏障会被插入(injected)到数据流中,并作为数据流的一部分随着数据流动。屏障并不会持有任何数据,而是和数据一样线性的流动。可以看到屏障将数据流分成了两部分数据(实际上是多个连续的部分),一部分是当前快照的数据,一部分下一个快照的数据。每个屏障会带有它的快照ID。这个快照的数据都在这个屏障的前面。从图上看,数据是从左向右移动(右边的先进入系统),那么快照n包含的数据就是右侧到下一个屏障(n-1)截止的数据,图中两个灰色竖线之间的部分,也就是part of checkpoint n。另外屏障并不会打断数的流动,因而屏障是非常轻量的。在同一个时刻,多个快照可以在同一个数据流中,这也就是说多个快照可以同时产生。
流程:如果是多个输入数据流,多个数据流的屏障会被同时插入到数据流中。快照n的屏障被插入到数据流的点(我们称之为Sn),就是数据流中一直到的某个位置(包含了当前时刻之前时间的所有数据),也就是包含的这部分数据的快照。
然后屏障开始向下流动。当一个中间的operator收到它的所有输入源的快照n屏障后,它就会向它所有的输出流发射一个快照n的屏障,一旦一个sink的operator收到所有输入数据流的屏障n,它就会向checkpoint的协调器发送快照n确认。当所有的sink都确认了快照n,系统才认为当前快照的数据已经完成。 一旦快照n已经执行完成,任务则不会再请求Sn之前的数据,因为此刻,这些数据都已经完全通过了数据流拓扑图。
第一步: Checkpoint Coordinator 向所有 source 节点 trigger Checkpoint. 然后Source Task会在数据流中安插CheckPoint barrier
第二步: source 节点向下游广播 barrier,这个 barrier 就是实现 Chandy-Lamport 分布式快照算法的核心,下游的 task 只有收到所有 input 的 barrier 才会执行相应的 Checkpoint
第三步: 当 task 完成 state 备份后,会将备份数据的地址(state handle)通知给 Checkpoint coordinator。
第四步: 下游的 sink 节点收集齐上游两个 input 的 barrier 之后,会执行本地快照,这里特地展示了 RocksDB incremental Checkpoint 的流程,首先 RocksDB 会全量刷数据到磁盘上(红色大三角表示),然后 Flink 框架会从中选择没有上传的文件进行持久化备份(紫色小三角)。
第五步: 同样的,sink 节点在完成自己的 Checkpoint 之后,会将 state handle 返回通知 Coordinator。
第六步: 最后,当 Checkpoint coordinator 收集齐所有 task 的 state handle,就认为这一次的 Checkpoint 全局完成了,向持久化存储中再备份一个 Checkpoint meta 文件。
原文链接:blog.csdn.net/weixin\_427…
Flink 处理 Barrier 分两种:
-
barrier 对齐
-
barrier 不对齐
对应的类
Savepoint原理
Flink Savepoint 作为实时任务的全局镜像,其在底层使用的代码和Checkpoint的代码是一样的,因为Savepoint可以看做 Checkpoint在特定时期的一个状态快照。
Flink 还提供了可以自定义的镜像保存功能,就是保存点(savepoints) 原则上,创建保存点使用的算法与检查点完全相同,因此保存点可以认为就是具有一些额外元数据的检查点 Flink不会自动创建保存点,因此用户(或外部调度程序)必须明确地触发创建操作 保存点是一个强大的功能。除了故障恢复外,保存点可以用于:有计划的手动备份,更新应用程序,版本迁移,暂停和重启应用,等等
Flink Savepoint 触发方式
Flink Savepoint 触发方式目前有三种:
1. 使用 flink savepoint 命令触发 Savepoint,其是在程序运行期间触发 savepoint。
2. 使用 flink cancel -s 命令,取消作业时,并触发 Savepoint。
3. 使用 Rest API 触发 Savepoint,格式为:**/jobs/:jobid /savepoints**
Flink Savepoint 注意点
1. 使用 flink cancel -s 命令取消作业同时触发 Savepoint 时,会有一个问题,可能存在触发 Savepoint 失败。比如实时程序处于异常状态(比如 Checkpoint失败),而此时你停止作业,同时触发 Savepoint,这次 Savepoint 就会失败,这种情况会导致,在实时平台上面看到任务已经停止,但是实际实时作业在 Yarn 还在运行。针对这种情况,需要捕获触发 Savepoint 失败的异常,当抛出异常时,可以直接在 Yarn 上面 Kill 掉该任务。
2. 使用 DataStream 程序开发时,最好为每个算子分配 `uid`,这样即使作业拓扑图变了,相关算子还是能够从之前的状态进行恢复,默认情况下,Flink 会为每个算子分配 `uid`,这种情况下,当你改变了程序的某些逻辑时,可能导致算子的 `uid` 发生改变,那么之前的状态数据,就不能进行复用,程序在启动的时候,就会报错。
3. 由于 Savepoint 是程序的全局状态,对于某些状态很大的实时任务,当我们触发 Savepoint,可能会对运行着的实时任务产生影响,个人建议如果对于状态过大的实时任务,触发 Savepoint 的时间,不要太过频繁。根据状态的大小,适当的设置触发时间。
4. 当我们从 Savepoint 进行恢复时,需要检查这次 Savepoint 目录文件是否可用。可能存在你上次触发 Savepoint 没有成功,导致 HDFS 目录上面 Savepoint 文件不可用或者缺少数据文件等,这种情况下,如果在指定损坏的 Savepoint 的状态目录进行状态恢复,任务会启动不起来。
checkpoint和savepoint的区别
Savepoint:Savepoint是由命令触发, 由用户创建和删除,保存点存储在标准格式存储中,并且可以升级作业版本并可以更改其配置。 用户必须提供用于还原作业状态的保存点的路径。
Checkpoint :Checkpoint被保存在用户指定的外部路径中, flink自动触发,当作业失败或被取消时,将保留外部存储的检查点。用户必须提供用于还原作业状态的检查点的路径。
flink序列化
Java序列化
Java的序列化机制一般是对象实现Serializable接口,并指定serialVersionUID。通过字节流的方式来实现序列化和反序列化。
serialVersionUID的作用是用来作为版本控制,如果serialVersionUID发生改变则会反序列化失败。
主要用途:
- 用于网络传输
- 对象深拷贝
- 用于将对象存储起来
缺点:
- 无法跨语言
- 序列化后码流太大
- 序列化性能太低
Flink的序列化
Flink实现了自己的序列化框架,并结合自身的内存模型,实现了对象的密集存储也高效操作。
- 可以看出这种序列化方式存储密度是相当紧凑的。其中 int 占4字节,double 占8字节,POJO多个一个字节的header,PojoSerializer只负责将header序列化进去,并委托每个字段对应的serializer对字段进行序列化。
- memory pool 内存池 memorySegment的数据结构,由两部分组成,一部分是存储key+pointer(完整二进制数据的指针以及定长的序列化后的key),第二部分是对象的二进制数据
- 使用内存池管理内存和使用二进制存储数据的的好处:
- 避免oom,所有的运行时数据结构和算法只能通过内存池申请内存,保证了其使用的内存大小是固定的,不会因为运行时数据结构和算法而发生OOM。在内存吃紧的情况下,算法(sort/join等)会高效地将一大批内存块写到磁盘,之后再读回来。因此,OutOfMemoryErrors可以有效地被避免。
- 节省内存空间,Java 对象在存储上有很多额外的消耗,使用二进制可以避免。
- 高效的二进制操作 & 缓存友好的计算,第一,交换定长块(key+pointer)更高效,不用交换真实的数据也不用移动其他key和pointer。第二,这样做是缓存友好的,因为key都是连续存储在内存中的,可以大大减少 cache miss(cpu读取L1,L2,L3高速缓存速度高于读取主内存速度几个数量级,使用key+pointer极大提高缓存L1,L2,L3命中率)
注意:Flink 中,排序会先用 key 比大小,这样就可以直接用二进制的key比较而不需要反序列化出整个对象。因为key是定长的,如果key相同(或者没有提供二进制key),那就必须将真实的二进制数据反序列化出来,然后再做比较。之后,只需要交换key+pointer就可以达到排序的效果,真实的数据不用移动。