Exactly Once 语义在 Flink 中的实现 | 青训营笔记

44 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第3天

exactly-once.png

1 数据流和动态表

如何在数据流上执行sql语句,说明流式处理中状态的概念。

  • 传统sql与数据流的区别

    • sql处理:是有界的,并且查询可以访问全部数据。
    • 数据流处理:是一个无限元组序列,查询访问不到所有的数据,且查询永不终止
  • 流处理流程

    • 数据流(Stream) -> 动态表(Dynamic Table) -> 连续的查询(Continuous Queries) -> 动态表 -> 处理后的数据流
  • 在流上定义动态表

    • 动态表:与表示批处理数据的静态表不同,动态表是随时间变化的。可以像查询静态批处理表一样查询它们。
  • 不同数据处理保证的语义

    • At-most-once: 出现故障的时候,啥也不做。数据处理不保证任何语义,但处理时延迟低;
    • At-least-once: 保证每条数据均至少被处理一次,但是一条数据可能存在重复消费。
    • Exactly-once: 最严格的处理语义,从输出结果来看,每条数据均被消费且仅消费一次,仿佛故障从未发生。

2 Exactly-once 和 Checkpoint

当故障发生时,Flink自身如何从故障中恢复,保证对数据消费的不丢不重。Flink的容错机制的核心部分是制作分布式数据流和操作算子状态的一致性快照。 Flink是通过Checkpoint来实现Exactly-Once,系统可以在发生故障时回滚。 Flink用于制作这些快照的机制在“分布式数据流的轻量级异步快照”中进行了描述。 它受到分布式快照的标准Chandy-Lamport算法的启发,专门针对Flink的执行模型而定制。

  • 状态快照与恢复

    我们的计算是有状态的,为了保证数据的不丢不重,我们要对设置一个数据恢复的时间点(”进行备份“)

  • 制作快照时间点

    数据恢复的时间点是有要求的:需要等待所有处理逻辑消费完成,source保留状态及之前的数据。否则的话,使用其他时间点可能会造成数据的丢失。

  • 简单制作快照的算法:

    • 1.暂停处理输入的数据
    • 2.等待后续所有处理算子消费当前已经输入的数据
    • 3.待2处理完后,作业所有算子复制自己的状态并保存到远端可靠存储
    • 4.恢复对输入数据的处理
  • Flink解决方案

    Flink的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;而基于分界线之后的数据导致的所有更改,就会被包含在之后的检查点中。

3 Flink端到端的Exactly-once语义

Flink本身的Checkpoint机制如何和外部存储结合实现端到端的不丢不重语义

严格意义的端到端的Exactly-once语义需要特殊的sink 算子实现---两阶段提交协议

  • 两阶段提交协议在Sink中的运用:

    • 事务开启:在 sink task 向下游写数据之前,均会开启一个事务,后续所有写数据的操作均在这个事务中执行,事务未提交前,事务写入的数据下游不可读;
    • 预提交阶段:JobManager 开始下发 Checkpoint Barrier,当各个处理逻辑接收到 barrier 后停止处理后续数据,对当前状态制作快照,此时 sink 也不在当前事务下继续处理数据(处理后续的数据需要新打开下一个事务)。状态制作成功则向JM成功的消息,失败则发送失败的消息;
    • 提交阶段:若 JM 收到所有预提交成功的消息,则向所有处理逻辑(包括 sink)发送可以提交此次务的消息,sink 接收到此消息后,则完成此次事务的提交,此时下游可以读到这次事务写入的数据;若 JM 有收到预提交失败的消息,则通知所有处理逻辑回滚这次事务的操作,此时 sink 则丢弃这次事务提交的数据。
特征SQL流处理
处理数据的有界性处理的表是有界的流是一个无限元组序列
处理数据的完整性执行查询可以访问完整的数据执行查询无法访问所有的数据
执行时间批处理查询产生固定大小结果后终止查询不断更新结果,永不终止

参考

一文读懂Apache Flink技术