Flink学习 - 7. Flink CheckPoint数据容错机制

有状态计算

Flink架构体系中,最重要的特性之一便是有状态计算。

什么是状态

状态计算一般是指: 在程序计算过程中,在程序内部储存计算所产生的中间结果,并提供给后续Function或者算子计算结果使用。

Flink有两种基本类型的State: Keyed State 和 Operator State(Non-Keyed State)。

Keyed State

是一种和Key相关的一种State,只能用于KeyedStream类型数据类型所对应的Functions和Operator上。

Operator State

Operator State和并行的算子实例绑定,与数据元素中的Key无关。Operator State支持 当算子实例并行度发生变化时自动重新分配状态数据。

状态管理

  • 托管状态(Manager State)
    由Flink Runtime中控制和管理状态数据,并将状态数据转换为内存Hash tables或RocksDB的对象存储,然后将这些状态数据通过内部的接口持久化到CheckPoints中,任务异常时可以通过这些状态数据恢复任务。

  • 原生状态(Row State)
    由算子自己管理数据结构,当触发CheckPoint过程中,Flink并不知道状态数据内部的数据结构,只是将数据转换成bytes数据存储在CheckPoints中,当从CheckPoint恢复任务是,算子自己再反序列化出状态的数据结构。

state的数据类型

Keyed State
  1. ValueState
  2. ListState
  3. ReducingState
  4. AggregatingState
  5. MapState
Operator State
  1. ListState
  2. BroadcastState

上述的State对象,仅仅用于与状态进行交互(更新,删除,清空等),而真正的状态值,有可能是存在内存、磁盘、或者其他分布式存储系统中。相当于我们只是持有了这个状态的句柄。实际上这些状态有三种存储方式:

MemoryStateBackend
FsStateBackend
RockDBStateBackend

Flink中使用state

  • 使用ValueState:
package com.jerome.flink.state;

import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

/**
 * This is Description
 *
 * @author Jerome丶子木
 * @date 2019/12/25
 */
public class ValueStateTest {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Tuple2<Integer, Long>> inputStream = env.fromElements(new Tuple2<Integer, Long>(2, 21L), new Tuple2<Integer, Long>(4, 1L), new Tuple2<Integer, Long>(5, 4L));

        SingleOutputStreamOperator<Tuple3<Integer, Long, Long>> leastValue = inputStream.keyBy(0).flatMap(new RichFlatMapFunction<Tuple2<Integer, Long>, Tuple3<Integer, Long, Long>>() {

            private ValueState<Long> leastValueState;

            @Override
            public void open(Configuration parameters) throws Exception {
                //创建ValueStateDescriptor,定意状态名称为leastValue,并指定数据类型
                ValueStateDescriptor<Long> leastValueStateDescriptor = new ValueStateDescriptor<>("leastValue", Long.class);
                //通过getRuntimeContext.getState获取State
                leastValueState = getRuntimeContext().getState(leastValueStateDescriptor);
            }

            @Override
            public void flatMap(Tuple2<Integer, Long> value, Collector<Tuple3<Integer, Long, Long>> out) throws Exception {
                //通过value方法从leastValueState中获取最小值
                Long leastValue = leastValueState.value();

                if (leastValue != null) {
                    //如果当前指标大于最小值,则直接输出数据元素和最小值
                    if (value.f1 > leastValue) {
                        out.collect(new Tuple3<>(value.f0, value.f1, leastValue));
                    } else {
                        //如果当前指标小于最小值,则更新状态中的最小值
                        leastValueState.update(value.f1);
                        //将当前数据中的指标作为最小值输出
                        out.collect(new Tuple3<>(value.f0, value.f1, value.f1));
                    }
                }else {
                    //如果状态中的值是空,则更新状态中的最小值
                    leastValueState.update(value.f1);
                    //将当前数据中的指标作为最小值输出
                    out.collect(new Tuple3<>(value.f0, value.f1, value.f1));
                }

            }
        });

        leastValue.print();

        env.execute("ValueStateTest");

    }
}

  • 通过sum所使用的StreamGroupedStream来说明keyedState使用方法:

  • FromElementsFunction类进行详解并分享如何在代码中使用 operator state:

CheckPoint

CheckPoint可以理解为: 将State状态数据持久化,注意这个CheckPoint是在同一时间点 Task/Operator的状态的全局快照。

CheckPoint是Flink在输入的数据集上间隔性的生成checkpoint barrier,并通过barrier将时间间隔段内的数据划分到相应的CheckPoint中。一旦Flink程序意外崩溃时,重新运行程序时可以有选择的从这些快照中恢复所有算子之前的状态,从而保证数据一致性。

单流的barrier

  1. checkpoint barrier会作为数据流的一部分注入到数据流中
  2. checkpoint barrier占用空间较小,非常轻量,对数据流任务影响相对较小。
  3. barrier严格遵循间隔性的产生,不会存在乱序
  4. 每一个checkpoint barrier都会携带对应的ID

并行的barrier

  1. Checkpoint指定触发生成时间间隔后,每当需要触发Checkpoint时,会向Flink程序运行时的多个分布式的Stream Source中插入一个Barrier标记。
  2. 这些Barrier会根据Stream中的数据记录一起流向下游的各个Operator。
  3. 多个输入流的Operator,需要在checkpoint 对齐(align)输入流,才会继续。当一个Operator接收到一个Barrier时,它会暂停处理Steam中新接收到的数据记录。因为一个Operator可能存在多个输入的Stream,而每个Stream中都会存在对应的Barrier,该Operator要等到所有的输入Stream中的Barrier都到达。当所有Stream中的Barrier都已经到达该Operator,这时所有的Barrier在时间上看来是同一个时刻点(表示已经对齐),在等待所有Barrier到达的过程中,Operator的Buffer中可能已经缓存了一些比Barrier早到达Operator的数据记录(Outgoing Records),
  4. 当对齐之后,该Operator会将数据记录(Outgoing Records)发射(Emit)出去,作为下游Operator的输入,最后将Barrier对应Snapshot发射(Emit)出去作为此次Checkpoint的结果数据。

Checkpoint 的执行机制

Apache Flink 进阶(三):Checkpoint 原理剖析与应用实践

下面阐述了state的存储,state是checkpoint进行持久化的主要角色。

Statebackend

下图阐释了目前 Flink 内置的三类 state backend,其中MemoryStateBackend和FsStateBackend在运行时都是存储在 java heap 中的,只有在执行 Checkpoint 时,FsStateBackend才会将数据以文件格式持久化到远程存储上。而RocksDBStateBackend则借用了 RocksDB(内存磁盘混合的 LSM DB)对 state 进行存储。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lEGH7QPv-1578641707672)(http://note.youdao.com/yws/res/22226/C34B91AB387C4CA5A2218BE5588429EA)]

HeapStateBackend

对于HeapKeyedStateBackend , 有两种实现:

  • 支持异步CheckPoint(默认):存储格式CopyOnWriteStateMap
  • 仅支持同步CheckPoint:存储格式NestedStateMap

特别在 MemoryStateBackend 内使用HeapKeyedStateBackend时,Checkpoint 序列化数据阶段默认有最大 5 MB 数据的限制

  • MamoryStateBackend
    state 数据保存在java堆内存中,执行checkpoint的时候,会把state的快照数据保存到jobmanager的内存中,基于内存的statebackend在生产环境不建议使用。

  • FsStateBackend
    state 数据保存在TaskManager的内存中,执行checkpoint的时候,会把state的快照数据保存到配置的文件系统中,可以使hdfs等分布式文件系统。

RocksDBStateBackend

对于RocksDBKeyedStateBackend,每个 state 都存储在一个单独的 column family 内,其中 keyGroup,Key 和 Namespace 进行序列化存储在 DB 作为 key。

  • RocksDBStateBackend
    RocksDB的存储方式略有不同,他会在本地文件系统中维护状态,state会直接写入到本地的RocksDB 当中。同时RocksDB需要配置一个源端的filesystem。

uri(一般是HDFS),在做checkpoint的时候,会把本地的数据直接复制到filesystem中,failover的时候从filesystem中恢复到本地。

RocksDB克服了state受内存限制的缺点,同时又能够持久化到远端文件系统中,比较适合在生产中使用。

CheckPoint机制详解

下图左侧是CheckPoint Coordinator,是整个CheckPoint的发起者,中间是由两个source,一个sink组成的Flink作业,最右侧的是持久化存储,在大部分用户场景中对应HDFS。

  1. 第一步,CheckPoint coordinator 向所有source节点trigger CheckPoint;

  1. 第二步, source 节点向下游广播barrier,这个barrier就是实现Chandy-Lamport分布式快照算法的核心,下游的task只有收到所有input的barrier才会执行费相应的CheckPoint。

  1. 第三部,当task完成state备份后,会将备份数据的地址(state handle)通知给CheckPoint coordinator。

  1. 第四步,下游的 sink 节点收集齐上游两个 input 的 barrier 之后,会执行本地快照,这里特地展示了 RocksDB incremental Checkpoint 的流程,首先 RocksDB 会全量刷数据到磁盘上(红色大三角表示),然后 Flink 框架会从中选择没有上传的文件进行持久化备份(紫色小三角)。

  1. 同样的,sink 节点在完成自己的 Checkpoint 之后,会将 state handle 返回通知 Coordinator。

  1. 最后,当 Checkpoint coordinator 收集齐所有 task 的 state handle,就认为这一次的 Checkpoint 全局完成了,向持久化存储中再备份一个 Checkpoint meta 文件。

Checkpoint 的 EXACTLY_ONCE 语义

为了实现 EXACTLY ONCE 语义,Flink 通过一个 input buffer 将在对齐阶段收到的数据缓存起来,等对齐完成之后再进行处理。而对于 AT LEAST ONCE 语义,无需缓存收集到的数据,会对后续直接处理,所以导致 restore 时,数据可能会被多次处理。下图是官网文档里面就 Checkpoint align 的示意图:

需要特别注意的是,Flink 的 Checkpoint 机制只能保证 Flink 的计算过程可以做到 EXACTLY ONCE,端到端的 EXACTLY ONCE 需要 source 和 sink 支持。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值