大数据最全Flink借助Kafka实现端到端精准一次_flink 如何保障数据一致性(2),2024年最新大数据开发面试题最新

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
import org.apache.flink.streaming.connectors.kafka.internals.KeyedSerializationSchemaWrapper;
import org.apache.flink.util.Collector;

import java.util.Properties;
import java.util.Random;

public class test {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

    //1.flink内部保证精准一次:设置检查点
    CheckpointConfig checkpointConfig = env.getCheckpointConfig();
    //10s内保存一次检查点
    checkpointConfig.setCheckpointInterval(10000);
    //保存检查点的超时时间为5秒钟
    checkpointConfig.setCheckpointTimeout(5000);
    //新一轮检查点开始前最少等待上一轮保存15秒才开始
    checkpointConfig.setMinPauseBetweenCheckpoints(15000);
    //设置作业失败后删除检查点(默认)
    checkpointConfig.enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION);
    //设置检查点模式为精准一次(默认)
    checkpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);

        env.setStateBackend(new FsStateBackend("file:///Users/xingxuanming/Downloads/flink-checkpoint/checkpoint"));

    Properties props_source = new Properties();
    props_source.setProperty("bootstrap.servers", "node1:9092");
    props_source.setProperty("group.id", "flink");
    props_source.setProperty("auto.offset.reset", "latest");
    //会开启一个后台线程每隔5s检测一下Kafka的分区情况
    props_source.setProperty("flink.partition-discovery.interval-millis", "5000");
    FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<>("flink\_kafka", new SimpleStringSchema(), props_source);
    kafkaSource.setStartFromLatest();
    //2.输入端保证:执行Checkpoint的时候提交offset到Checkpoint(Flink用)
    kafkaSource.setCommitOffsetsOnCheckpoints(true);
    DataStreamSource<String> kafkaDS = env.addSource(kafkaSource);

    SingleOutputStreamOperator<Tuple2<String, Integer>> wordAndOneDS = kafkaDS.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
        @Override
        public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {
            //value就是每一行
            String[] words = value.split(" ");
            for (String word : words) {
                Random random = new Random();
                int i = random.nextInt(5);
                if (i > 3) {
                    System.out.println("出bug了...");
                    throw new RuntimeException("出bug了...");
                }
                out.collect(Tuple2.of(word, 1));
            }
        }
    });
    KeyedStream<Tuple2<String, Integer>, Tuple> groupedDS = wordAndOneDS.keyBy(0);
    SingleOutputStreamOperator<Tuple2<String, Integer>> aggResult = groupedDS.sum(1);
    SingleOutputStreamOperator<String> result = (SingleOutputStreamOperator<String>) aggResult.map(new RichMapFunction<Tuple2<String, Integer>, String>() {
        @Override
        public String map(Tuple2<String, Integer> value) throws Exception {
            return value.f0 + ":::" + value.f1;
        }
    });

    //3.输出端保证
    Properties props_sink = new Properties();
    props_sink.setProperty("bootstrap.servers", "node1:9092");
    //3.1 设置事务超时时间,也可在kafka配置中设置
    props_sink.setProperty("transaction.timeout.ms", 1000 \* 5 + "");
    FlinkKafkaProducer<String> kafkaSink = new FlinkKafkaProducer<>(
            "flink\_kafka2",
            new KeyedSerializationSchemaWrapper<String>(new SimpleStringSchema()),
            props_sink,
            //3.2 设置输出的的语义为精准一次
            FlinkKafkaProducer.Semantic.EXACTLY_ONCE
    );
    result.addSink(kafkaSink);

    env.execute();
   }
 进行测试:

 先确认下,这里不用关闭应用程序,再重启应用程序来模拟故障,而是**程序里模拟故障,用随机数直接throw Exeception**

 进入到kafka的bin目录下


	+ 启动zookeeper
	
	 ./zookeeper-server-start.sh …/config/zookeeper.properties &
	+ 启动kafka
	
	 ./kafka-server-start.sh …/config/server.properties &
	+ 创建主题
	
	 kafka-topics.sh --bootstrap-server localhost:2181 --create --replication-factor 2 --partitions 3 --topic flink\_kafka2
	+ 打开控制台生产者
	
	 kafka-console-producer.sh --broker-list localhost:9092 --topic flink\_kafka
	+ 打开控制台消费者
	
	 kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic flink\_kafka2来看最后的效果:

 此代码主要功能是对输入的字符串做分区和求和操作,例如,传入hello,world,hello三条数据,按照字符串进行分区,分为hello,world两个分区,求和是按照分区内所有汇总的字段进行求和,最后结果是(hello,2) (world,1)


![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TjbpOjHL-1681811696794)(image-20230414195452057.png)]](https://img-blog.csdnimg.cn/52480310f07e429790778b0deaeed1b0.png) 我们假设,来了三条数据,hello,world,hello,处理了两条数据,此时算子状态为(hello,1) (world,1) 第三条数据来的时候“恰好”故障,检查点保存的偏移量是第一条数据hello,算子里保存的是(hello,1) 那么继续从第二条数据world开始消费,第二条数据处理成功,此时算子里保存的(hello,1) (world,1),第三条数据处理成功,最终效果为(hello,2) (world,1)  
 **3)最后补充下完整的流程:**



> 
> **(1)启动检查点保存**
> 
> 
> 检查点保存的启动,标志着我们进入了两阶段提交协议的“预提交”阶段。但此时现在还没有具体提交的数据。
> 
> 
> jobManager 通知各个 TaskManager 启动检查点保存,Source 任务会将检查点分界线(barrier)注入数据流。这个 barrier 可以将数据流中的数据,分为进入当前检查点的集合和进入下一个检查点的集合。
> 
> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/eeae75767f464eed9634cd4fa0e7c8a8.png)
> 
> 
> **(2)算子任务对状态做快照**
> 
> 
> 分界线(barrier)会在算子间传递下去。每个算子收到 barrier 时,会将当前的状态做个快 照,保存到状态后端。
> 
> 
> Source 任务将 barrier 插入数据流后,也会将当前读取数据的偏移量作为状态写入检查点,存入状态后端;然后把 barrier 向下游传递,自己就可以继续读取数据了。 接下来 barrier 传递到了内部的 Window 算子,它同样会对自己的状态进行快照保存,写入远程的持久化存储。
> 
> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/c8ab6b0a7a6b4b4990c1eb57a8ea40ef.png)
> 
> 
> **(3)Sink 任务开启事务,完成最后的预提交**
> 
> 
> 分界线(barrier)终于传到了 Sink 任务,这时 Sink 任务会开启一个事务。接下来到来的所有数据,Sink 任务都会通过这个事务来写入 Kafka。这里 barrier 是检查点的分界线,也是事务的分界线。当处理barrier的时候,实际上之前的数据都已经全部处理完了,不用管之前的,直接开启了新的事务。 对于 Kafka 而言,提交的数据会被标记为“未确认”(uncommitted)。整个过程就是所谓 的“预提交”(pre-commit)
> 
> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/020350c75ed749d9abf2f6541207b019.png)
> 
> 
> **(4)检查点保存完成,提交事务**
> 
> 
> 当所有算子的快照都完成,也就是这次的检查点保存最终完成时,JobManager 会向所有任务发确认通知,告诉大家当前检查点已成功保存。
> 
> 
> ​ 当 Sink 任务收到确认通知后,就会正式提交之前的事务,把之前“未确认”的数据标为 “已确认”,接下来就可以正常消费了。 在任务运行中的任何阶段失败,都会从上一次的状态恢复,所有没有正式提交的数据也会回滚。这样,Flink 和 Kafka 连接构成的流处理系统,就实现了端到端的 exactly-once 状态一致性。
> 
> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/cb70de08ec6c4a8da6c3c22c0d5aade2.png)
> 
> 
> 


## 二、前置知识


### ⭐️1.检查点(快照)


#### **(1)概念:**



> 
> 其实就是所有任务的状态在**某个时间点的一个快照**(一份拷贝)。Flink 会**定期保存检查点**,在检查点中会记录每个算子的 id 和状态;**如果发生故障,Flink 就会用最近一次成功保存的检查点来恢复应用的状态**,重新启动处理流程,就如同“读档”一样。
> 
> 
> ⭐️**默认情况下,检查点是被禁用的**,需要在代码中手动开启。
> 
> 
> 


#### **(2)拓展问题:**



> 
> **1.什么时候进行保存?**
> 
> 
> 
> > 
> > **当每隔一段时间**检查点保存操作被触发时,就把每个任务当前的状态复制一份,按照一定的逻辑结构放在一起持久化保存起来,就构成了检查点。 在 Flink 中,检查点的保存是周期性触发的,**间隔时间可以进行设置。**
> > 
> > 
> > 
> 
> 
> **2.如何保存以及恢复的?**
> 
> 
> 举个例子,看看完整的流程图:
> 
> 
> 
> > 
> > 首先,我们**已经确认(hello,hello,world)三条数据**在**所有算子任务都处理完**了,那么**每个算子任务会把当前处理过程中的值**保存到外部存储中,注意是**每个算子任务**。
> > 
> > 
> > 由于全部处理完了3条数据,所以source保存的偏移量事3
> > 
> > 
> > 第一个分区,处理了两条hello数据,所以保存的数据就是hello,2
> > 
> > 
> > 第二个分区处理了一条world数据,所以保存的数据就是world,1
> > 
> > 
> > ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8uLxBQd7-1681811696788)(image-20230413152858608.png)]](https://img-blog.csdnimg.cn/3586f60c40674985abc43b43e87f5df6.png)
> > 
> > 
> > 此时发生故障,系统宕机
> > 
> > 
> > ![在这里插入图片描述](https://img-blog.csdnimg.cn/eb76e64524f44743be1019ffa45524a6.png)
> > 
> > 
> > 重启应用后,所有算子的状态都是空的,第一件事就是读取检查点,也就是刚才检查点保存的值再读取到内存中
> > 
> > 
> > ![在这里插入图片描述](https://img-blog.csdnimg.cn/33223b73407a4bed8865f5ab72dcba2b.png)  
> >  ![ [](https://img-blog.csdnimg.cn/a3af959c21f0434981d04bb47f7def32.png)
> > 
> > 
> > 第二件事,通过数据源的偏移量,**进行重放数据**,因为处理完3条数据了,所以从第三条数据后面开始继续处理。
> > 
> > 
> > ![在这里插入图片描述](https://img-blog.csdnimg.cn/e5adf717c2fe404286392191f0a9e32e.png)
> > 
> > 
> > ![在这里插入图片描述](https://img-blog.csdnimg.cn/c2f2ed55edaf466491234a0a9e8e3599.png)  
> >  ![在这里插入图片描述](https://img-blog.csdnimg.cn/68dae5b00cf44106859e462db01f78d3.png)
> > 
> > 
> > 
> 
> 
> 3.为什么**检查点保存时机**选择**所有算子任务处理完同一批数据**?
> 
> 
> 首先需要思考的一个问题:在算子A保存检查点的过程中,算子B正在处理新的数据,出现宕机,那我恢复的时候虽然明确知道哪条数据没有处理完,但是不确定执行到哪一个算子了,所以只能从头source放入数据,然后到算子A执行,但是算子A可能算出来这条数据的中间状态进行保存了, 相当于算子A要对这条数据处理两次。
> 
> 
> 举例:比如数据a,要进行处理1(求和),处理2(输出到存储文件)阶段,发生故障的时候在处理2阶段,处理1阶段已经保存了处理1阶段的值,难道恢复的时候,处理1阶段还要对数据的a再操作一次吗?
> 
> 
> 那我不选择处理完同一批数据,有没有什么其他解决方案?有,比较粗暴的方案。
> 
> 
> 
> > 
> > **粗暴**的解决方案:
> > 
> > 
> > 最简单的想法是,可以在某个时刻“按下暂停键”,让**所有任务停止处理数据**。**无论是检查点前的还是检查点后面的**,都不许处理保存检查点时来的新的数据,就算宕机故障,恢复后开始处理,大家的状态都还是准确的。
> > 
> > 
> > 然而仔细思考就会发现这有很多问题。这种想法其实是粗暴地“停止一切来拍照”,在保存检查点的过程中,**任务完全中断了,这会造成很大的延迟**;我们之前为了实时性做出的所有设计就毁在了做快照上。
> > 
> > 
> > 
> 
> 
> 4.那**怎么确认所有算子都处理完同一批数据**?直接按照**最后一个sink算子任务**输出成功的**数据作为偏移量**不可以吗?
> 
> 
> 
> > 
> > **完全不可以**,最后一个sink算子任务输出的时候,虽然保证之前的数据已经全部处理完了。**但是有个问题是**,你在输出的时候,**上一个算子任务开始处理新的数据了,整个过程大家都是异步的,你sink的时候还能不让其他算子继续处理新数据了吗**,所以真正要用到的偏移量。**一定是“特殊标记”的上一条数据**。
> > 
> > 
> > 那特殊标记在Flink中有概念吗?
> > 
> > 
> > 有,看下面的检查点算法实现。
> > 
> > 
> > 
> 
> 
> **5.保存检查点的时候,新来的数据到底该怎么办?**
> 
> 
> 
> > 
> > **先缓存起来**,不着急处理,**等到检查点保存后再继续处理**。
> > 
> > 
> > 我们可以在source数据中插入一条分界线,一旦所有算子遇到这个分界线就先把分界线之前的数据都处理完,马上开始各自保存当前的快照,保存完往下个算子传递,相当于增加一个约束,“特殊标记前面的一定是都处理完的数据”)。
> > 
> > 
> > 
> 
> 
> **6.那么你既然要缓存不处理,不还是粗暴的方案?不让新数据处理执行,有什么意义吗?**
> 
> 
> 
> > 
> > 有,记住,是每个算子任务都保留“自己”的快照,算子A保留快照的时候,不影响算子B处理数据,同样的,算子A已经保留完快照,算子B保留快照的时候,算子A可以允许处理新的数据。  
> >  相当于避免了粗暴方案中的“stop the world”
> > 
> > 
> > 
> 
> 
> **7.那如果任务某个算子任务是并发情况呢?你怎么传递这个特殊标记?**
> 
> 
> 
> > 
> > 还是要看检查点算法,下游任务必须接收所有上游并发任务的标记过来,才能开始保存快照。
> > 
> > 
> > 
> 
> 
> 8.既然选择**所有任务都处理完一个相同数据**,如果**有其中一个任务没保存下状态**,其他任务都保存了怎么办?
> 
> 
> 
> > 
> > 构建一个“事务”,只要有一个任务没保存状态,所有任务保存的状态直接删除,重新发送分界线,重新开始保存
> > 
> > 
> > 
> 
> 
> 


#### **(3)检查点算法:**



> 
> **为什么需要检查点算法?**
> 
> 
> 刚才看到了检查点的保存,是所有算子任务都接受到了“保存快照”的标记,那么,谁来发起保存检查点,谁来通知所有算子任务进行保存呢?  
>  带着这个疑问,先来说说,这个“保存快照的标记”是什么?
> 
> 
> **1)检查点分界线(Barrier)**
> 
> 
> 
> > 
> > 与水位线很类似,检查点分界线也是一条特殊的数据,由 Source 算子注入到常规的数据流中,它的位置是限定好的,不能超过其他数据,也不能被后面的数据超过。检查点分界线中**带有一个检查点 ID**,这是当**前要保存的检查点的唯一标识**。
> > 
> > 
> > 这样,分界线就将一条流逻辑上分成了两部分:**分界线之前到来的旧数据**,都数据当前检查点应该保存的,而基于分界线之后的新数据,因为不确定有没有处理,则会被包含在之后的检查点中。
> > 
> > 
> > ⭐️**这个分界线如何进行传递?**
> > 
> > 
> > * JobManager中的检查点协调器 定期向 TaskManager 发出指令,要求保存检查点
> > * TaskManager 会让所有的 Source 任务把自己的偏移量(算子状态)保存起来,并将带有检查点 ID 的分界线(barrier)插入到当前的数据流中,**先等待Source任务把快照保存后,并向下游子任务传递barrier。之后 Source 任务就可以继续读入新的数据了**。
> > * 每个算子任务只要处理到这个 barrier,就把当前的状态进行快照。
> > 
> > 
> > ![在这里插入图片描述](https://img-blog.csdnimg.cn/1111916411684b5499cb6892eac6c088.png)
> > 
> > 
> > 
> 
> 
> **2)分布式快照算法**
> 
> 
> 
> > 
> > 我们已经知道了这个特殊字段叫做分界线,那么通知下游的算子任务?此时需要分布式快照算法
> > 
> > 
> > ⭐️⭐️算法的核心三个原则:
> > 
> > 
> > * 当上游任务向多个并行下游任务发送 barrier 时,需要**广播出去**
> > * 而当**多个上游任务**向**同一个下游任务**传递 barrier 时,需要在下游任务执行“分界线对齐”(barrier alignment)操作,说白了需要**等到所有上游任务发送给自己的 barrier 都到齐**,才开始“快照”的保存。
> > * ⭐️在barrier到来后,**保存快照期间 新来的数据会进行缓存,不会处理也不会保存到检查点**, 直到保存完快照才进行继续进行处理。
> > 
> > 
> > ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cKp6B9i-1681811696793)(image-20230412204913560.png)]](https://img-blog.csdnimg.cn/8b52940d64ca4e449c29b78a88f0c7eb.png)
> > 
> > 
> > * 当**多个上游任务**向**同一个下游任务**传递 barrier什么意思?
> > 
> >  如上图所示,假设 sum1获取到map1,map2的barrier,就开始快照保存,sum2只获取map1的barrier,必须等待map2发送过来barrier才能开始快照保存
> > * ⭐️那等待所有上游任务发送给自己的 barrier的过程中,有新的数据进来怎么办?
> > 
> >  **也会保存到检查点中**,直到收到全部的barrier到来后,才不会放到检查点中。
> > 
> > 
> > 
> 
> 
> 


#### **(4)使用:**



> 
> **1)启用检查点:**
> 
> 
> * enableCheckpointing 传入一个保存检查点的间隔
> 
> 
> 
> ```
> StreamExecutionEnvironment env =  StreamExecutionEnvironment.getExecutionEnvironment(); 
> // 每隔1秒启动一次检查点保存 
> env.enableCheckpointing(1000); 
> 
> ```
> 
> **2)配置检查点存储位置:**
> 
> 
> * setCheckpointStorage
> 
> 
> 对于实际生产应用,我们**一般会将 CheckpointStorage 配置为高可用的分布式文件系统**(HDFS,S3 等)。
> 
> 
> 
> ```
> env.getCheckpointConfig().setCheckpointStorage("hdfs://namenode:40010/flink/checkpoints"); 
> 
> ```
> 
> ⭐️3)其他参数:
> 
> 
> * **检查点模式** setCheckpointingMode
> 
>  EXACTLY\_ONCE **精准一次(默认)**  
>  AT\_LEAST\_ONCE 最少一次
> 
>  对于大多数低延迟的流处理程序,at-least-once 就够用了,而且处理效率会更高。
> * **检查点保存的超时时间** setCheckpointTimeout
> 
>  注意enableCheckpointing是多久触发一次检查点保存,**setCheckpointTimeout**是允许多长时间内保存完毕。
> * **新一轮检查点保存开始,等待上一个检查点的时间** setMinPauseBetweenCheckpoint
> 
>  ⭐️如果设置的检查点保存超时时间小于保存检查点的时间间隔,这个设置就是生效的。
> 
>  这个跟enableCheckpointing的区别在于enableCheckpointing是多久触发一次检查点保存,setMinPauseBetweenCheckpoint是上一个检查点已经开始了,但是迟迟不久保存下来,第二个检查点必须等多长时间才能开始保存检查点。
> * **作业失败时是否删除检查点** enableExternalizedCheckpoints
> 
>  RETAIN\_ON\_CANCELLATION **保留(默认)**
> 
>  DELETE\_ON\_CANCELLATION 直接删除
> * **检查点保存失败时停掉作业** setFailOnCheckpointingErrors
> 
>  true **停掉作业(默认)**
> 
>  false 不停掉作业
> * enableUnalignedCheckpoints
> 
>  不再执行检查点的分界线对齐操作,启用之后可以大大减少产生背压时的检查点保存时间。这个设置要求检查点模式(CheckpointingMode)必须为 exctly-once,并且并发的检查点个数为 1
> 
> 
> 
> ```
> StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
> //获取检查点
> CheckpointConfig checkpointConfig = env.getCheckpointConfig();
> 
> //设置检查点的保存,用hdfs
> checkpointConfig.setCheckpointStorage("hdfs://my/checkpoint/dir");
> 
> //EXACTLY\_ONCE表示精准一次 AT\_LEAST\_ONCE表示最少一次
> checkpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
> 
> //检查点保存的超时时间,超过10秒直接报失败
> checkpointConfig.setCheckpointTimeout(10000L);
> 
> //检查点的保存时间间隔,20秒后开始下一轮的检查点保存
> checkpointConfig.setMinPauseBetweenCheckpoints(20000L);
> 
> //最多并发几个运行的检查点
> checkpointConfig.setMaxConcurrentCheckpoints(2);
> 
> //启用不对齐的检查方式
> checkpointConfig.enableUnalignedCheckpoints(); 
> 
> //开启检查点的外部持久化,而且[默认]在作业失败的时候不会自动清理
> checkpointConfig.enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
> 
> ```
> 
> 


#### ⭐️(5)保存点



> 
> **概念:**
> 
> 
> * 什么是保存点?
> 
>  也是一个存盘的备份,它的原理和算法与检查点完全相同,只是多了一些额外的元数据。
> * 跟检查点的区别在哪里?
> 
>  检查点是由 Flink 自动管理的,定期创建,发生故障之后自动读取进行恢复,这是一个“自动存盘”的功能
> 
>  而**保存点不会自动创建**,必须由用户明确地手动触发保存操作,所以就是“手动存盘”。
> 
> 
> **适用场景:**
> 
> 
> * 版本管理和归档存储
> 
>  对重要的节点进行手动备份,设置为某一版本,归档(archive)存储应用程序的状态。
> * **更新应用程序**
> 
>  我们不仅可以在应用程序不变的时候,更新 Flink 版本;还可以直接更新应用程序。前提是程序必须是兼容的,也就是说更改之后的程序,状态的拓扑结构、数据类型是不变的,这才能正常从之前的保存点去加载。
> * **调整并行度**
> 
>  如果应用运行的过程中,发现需要的资源不足或已经有了大量剩余,也可以通过从保存点重启的方式,将应用程序的并行度增大或减小。
> 
> 
> **使用:**
> 
> 
> * 代码中设置uid
> 
>  对于**没有设置 ID 的算子,Flink 默认会自动进行设置**,所以在重新启动应用后可能会导致 ID 不同而无法兼容以前的状态。所以为了方便后续的维护,一定要在程序中**为每一个算子手动指定 ID**。
> 
>  
> ```
> DataStream<String> stream = env 
>   .addSource(new StatefulSource()) 
>   .uid("source-id") 
>   .map(new StatefulMapper()) 
>   .uid("mapper-id") 
>   .print(); 
> 
> 
> ```
> * **保存**
> 
> 
> 	+ **savepoint** 表示要开始执行保存点
> 	+ **jobId (必填)** 就是当前作业id名称
> 	+ \*\*targetDirectory(选填)\*\*表示持久化的目录
> 	
> 	 不填则使用conf下的state.savepoints.dir 目录
> ```
> bin/flink savepoint :jobId [:targetDirectory] 
> 
> ```
> * **恢复**
> 
> 
> 	+ 这里只要增加一个-s 参数,指定保存点的路径就可以了
> ```
> bin/flink run -s :savepointPath [:runArgs] 
> 
> ```
> 
> 
> 


### 2.持久化保存状态


**(1)概念**



> 
> 状态的存储、访问以及维护,都是由一个可插拔的组件决定的,这个组件就叫作状态后端(state backend)。状态后端主要负责两件事:一是**本地的状态管理**,二是**将检查点(checkpoint)写入远程的持久化存储**。
> 
> 
> 


**(2)流程**



> 
> * 在应用进行检查点保存时,状态后端会在 JobManager 向所有 TaskManager 发出触发检查点的命令。
> * TaskManger 收到之后,将当前任务的所有状态进行快照保存,持久化到远程的存储介质中。
> * 完成之后向 JobManager 返回确认信息。
> * 当 JobManger 收到所有 TaskManager 的返回信息后,就会确认当前检查点成功保存。
> 
> 
> **而这一切工作的协调,就是“状态后端” 来完成的**
> 
> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/30564580111641c0a21bda69bb13f03d.png)
> 
> 
> 


**(3)分类**


* **⭐️默认⭐️** 哈希表状态后端(HashMapStateBackend)



> 
> 将本地状态全部放入内存,**保存在 Taskmanager 的 JVM 堆(heap)上**,所以底层是一个哈希表(HashMap),这种状态后端也因此得名。
> 
> 
> 优点:
> 
> 
> 
> 	+ **读写速度非常快**
> 缺点:
> 
> 
> 
> 	+ 占用taskManager运行内存。
> **配置**:
> 
> 
> 文件配置:
> 
> 
> 
> ```
> # 默认状态后端 
> state.backend: hashmap 
> # 存放检查点的文件路径 
> state.checkpoints.dir: hdfs://namenode:40010/flink/checkpoints 
> 
> ```
> 
> 代码配置:
> 
> 
> 
> 	+ 直接设置**StateBackend**为**HashMapStateBackend**
> 
> ```
> StreamExecutionEnvironment 	env =StreamExecutionEnvironment.getExecutionEnvironment();
> env.setStateBackend(new HashMapStateBackend());
> 
> 
> ```
> 
>
* 内嵌 RocksDB 状态后端(EmbeddedRocksDBStateBackend)



> 
> 会将处理中的数据全部放入 RocksDB 数据库中,RocksDB 默认存储在 TaskManager 的本地数据目录里。
> 
> 
> 优点:
> 
> 
> 
> 	+ **不会占用taskManager运行内存**
> 	+ 可以根据需要对磁盘空间进行扩展,**适合海量状态的存储**。
> 缺点:
> 


![img](https://img-blog.csdnimg.cn/img_convert/47b671546fb36efd66fe8004ea988456.png)
![img](https://img-blog.csdnimg.cn/img_convert/1abda8285117320bbe7a89d6e71c7c95.png)
![img](https://img-blog.csdnimg.cn/img_convert/29bcc802aaec37546d110cfdb740a8a7.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

nEnvironment.getExecutionEnvironment();
> env.setStateBackend(new HashMapStateBackend());
> 
> 
> ```
> 
>
* 内嵌 RocksDB 状态后端(EmbeddedRocksDBStateBackend)



> 
> 会将处理中的数据全部放入 RocksDB 数据库中,RocksDB 默认存储在 TaskManager 的本地数据目录里。
> 
> 
> 优点:
> 
> 
> 
> 	+ **不会占用taskManager运行内存**
> 	+ 可以根据需要对磁盘空间进行扩展,**适合海量状态的存储**。
> 缺点:
> 


[外链图片转存中...(img-zZLXzmzI-1715419627282)]
[外链图片转存中...(img-39sX8giB-1715419627282)]
[外链图片转存中...(img-q26mwPXN-1715419627282)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值