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

101 阅读3分钟

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

一、数据流和动态表

1.1 传统SQL和流处理

流(事件流)

流指的是像Kafka、Kinesis或Pulsar这样的消息中介,它们将数据作为事件或消息的连续流来处理。

事件流处理一切,从交易到用户在网站或移动应用程序上的行动、物联网传感器数据、服务器的指标,甚至是传统数据库上的活动,都通过 change data capture.

SQL

在流的背景下,SQL为用户提供了一种声明性语言,用于:

  • 创建视图,将流中的原始数据连接、过滤和分组为更有用的输出(CREATE MATERIALIZED VIEW)
  • 从源和视图中选择数据(SELECT)

1.2 数据流和动态表转换

流、动态表、连续查询的关系如下图所示: image.png

① 流将转换为动态表;
② 在动态表上评估连续查询,生成新的动态表;
③ 生成的动态表将转换回流。

1.3 连续查询

连续查询作用于动态表并又会产生动态表了连续查询不会终止并会根据其输入表(动态表)上的更新来更新其结果表(动态表)。

image.png

二、Exactly-Once 和 Checkpoint

2017年12月,apache flink 1.4.0发布。其中有一个里程碑式的功能:两步提交的sink function(TwoPhaseCommitSinkFunction,relevant Jira here)。TwoPhaseCommitSinkFunction 就是把最后写入存储的逻辑分为两部提交,这样就有可能构建一个从数据源到数据输出的一个端到端的exactly-once语义的flink应用。当然,TwoPhaseCommitSinkFunction的数据输出包括apache kafka 0.11以上的版本。flink提供了一个抽象的TwoPhaseCommitSinkFunction类,来让开发者用更少的代码来实现端到端的exactly-once语义。

Flink的 checkpoint在保证exactly-once是内部应用exactly-once,不需要重复计算等 Flink是通过两步提交协议来保证从数据源到数据输出的exactly-once语义(外部)

当所有算子都完成他们的快照时,进行正式提交操作

当任意子任务在预提交阶段失败时,其他任务立即停止,并回滚到上一次成功快照的状态。

在预提交状态成功后,外部系统需要完美支持正式提交之前的操作。如果有提交失败发生,整个flink应用会进入失败状态并重启,重启后将会继续从上次状态来尝试进行提交操作。

三、Flink端到端的 Exactly-once语义

虽然Flink 通过强大的异步快照机制和两阶段提交,实现了“端到端的精确一次语义”。但端到端的精确一次还依赖其他的外部系统。

“端到端(End to End)的精确一次”,指的是 Flink 应用从 Source 端开始到 Sink 端结束,数据必须经过的起始点和结束点。

Flink 自身是无法保证外部系统“精确一次”语义的,所以 Flink 若要实现所谓“端到端(End to End)的精确一次”的要求,那么外部系统必须支持“精确一次”语义;然后借助 Flink 提供的分布式快照两阶段提交才能实现。

整个过程可以总结为下面四个阶段:

  • 一旦 Flink 开始做 checkpoint 操作,那么就会进入 pre-commit 阶段,同时 Flink JobManager 的Coordinator会将检查点 Barrier 注入数据流中 ;
  • 当所有的 barrier 在算子中成功进行一遍传递,并完成快照后,则 pre-commit 阶段完成;
  • 等所有的算子完成“预提交”,就会发起一个commit“提交”动作,但是任何一个“预提交”失败都会导致 Flink 回滚到最近的 checkpoint;
  • pre-commit 完成,必须要确保 commit 也要成功。

Flink在这个过程中的几个关键Operator:

  1. SouceOperator从Kafka消费消息并记录offset。
  2. TransformationOperator对数据进行处理转换并作Checkpoint。
  3. SinkOperator将结果写入Kafka。