1. Flink容错机制的核心是检查点(checkpoint),检查点可以看作是所有任务状态在同一个时间点的一个快照,它的触发是周期性的。
2. 检查点所保存的所有任务的状态具体怎么定义?=> 当所有任务都恰好处理完同一个数据输入的时候,我们就对这一时间点的所有任务的状态进行保存。
3. 如何让每个任务“认出”触发检查点的那个数据?=> 借鉴水位线的设计,当jobmanager向taskmanager发出触发检查点指令时,taskmanager会让所有的source任务把自己的偏移量(算子状态)进行保存,并在当前的数据流中插入一条特殊的数据(带有检查点id的分界线(barrier)),在向下游传递数据的过程中,就涉及到了两种情况:(1)上游任务向多个并行下游任务发送barrier(如sum算子进行keyby聚合时,一个上游任务可能有多个key,每个key需要发送到不同的下游任务中),此时需要将barrier广播到下游的所有并行子任务中 (2)多个上游任务向同一个下游任务传递barrier(如多个上游任务中具有相同的key, 进行sum聚合时需要向同一个下游任务传递barrier),此时需要在下游任务中执行“分界线对齐”(barrier alignment),也就是等到所有上游任务的barrier都到齐,才开始下游任务状态的保存(注意在等待分界线对齐的过程中,先到达的上游需要等待(否则继续计算状态会发生改变,应为barrier之后的数据就是下一个检查点需要保存的了),因此对先到达的上游barrier之后继续传递过来的数据做缓存,等到分界线对齐,状态保存后在取出来做处理)。
4. 检查点保存算法的具体流程总结如下:
(1)JobManager周期性向TaskManager发送一条带有检查点id的指令,触发检查点;
(2)TashManager收到指令,向Source任务中插入barrier,并将source任务的偏移量(状态)持久化保存;
(3)source状态持久化完成,向JobManager确认当前任务检查点保存成功,barrier继续随着数据流向下游任务传递;
(4)向下游多个并行子任务广播分界线,并执行分界线对齐操作;
(5)分界线对齐完成后,将当前任务状态持久化保存,并向jobmanager确认保存完毕;
(6)先处理分界线对齐过程中缓存的数据,再继续正常处理。
(7)发生故障时则从最近的检查点保存的状态进行恢复,实现了EXACTLY-ONCE。
5. 状态一致性三种级别:最多一次(AT-MOST-ONCE):任务发生故障啥也不干,数据只在正常情况下被处理一次,故障直接丢掉;至少一次(AT-LEAST-ONCE):不丢数据,要求故障发生时能重放数据,则部分数据会被重复处理,在某些场景下不会影响最终计算结果的准确性(如UV),但如PV、wordcount等类似场景就会导致结果不一致;(精确一次)EXACTLY-ONCE:数据不仅不会被丢失,且反应在最终输出结果上只能有一次统计, Flink的checkpoint就实现了EXACTLY-ONCE。
6. 端到端的状态一致性,包括数据源输入端、流处理器和输出端(外部存储系统)三个部分的状态一致性,取决于三者之中最弱的一环。Flink已经实现了EXACTLY-ONCE,因此端到端的状态一致性就取决于输入端以及输出端。
(1)输入端:输入端主要用来保证发生故障时,数据不丢,因此数据源输入端只要能够重放数据即可,如Kafka的偏移量,这样就至少达到了AT-LEAST-ONCE一致性语义的要求,在此基础上才能实现端到端的EXACTLY-ONCE。
(2)输出端:输出端想要实现EXACTLY-ONCE所面临的困难?=> 数据可能重复写入外部系统。
=> 幂等写入、事务写入。
(3)幂等写入。所谓幂等操作,就是指一个操作可以被重复执行,但只有第一次执行导致结果更改,后面再执行不会对结果起作用,如HashMap的相同键值对数据插入。这需要输出端的外部存储系统支持幂等写入,那么即使重复写入也没关系,如Redis中的键值存储。
(4)事务写入。基本思想是将事务与检查点绑定,用一个事务来进行数据向外部系统的写入,具体而言,当Sink任务遇到barrier时,在保存当前状态的同时开启一个事务,通过这个事务将数据写入。等到当前状态保存成功则提交事务,写入的数据就可用了;如果当前状态保存失败则回滚,写入的数据也会被撤销。
事务写入的具体实现方式有两种:预写日志(WAL)、两阶段提交(2PC)。
预写日志将结果数据作为日志状态先保存起来,进行检查点保存时一并做持久化保存,在收到检查点完成通知时将所有结果一次性写入外部系统。预写日志不需要外部系统支持事务。但是批量写入可能会影响性能。
两阶段提交即先进行“预提交”,但此时数据为未确认状态(不可用),如写入到Kafka但不能被消费;等到检查点保存完成后再正式提交,数据可以被消费;两阶段提交需要外部系统提供事务支持。