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

155 阅读7分钟

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

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

01. 数据流和动态表

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

1.1 随处可见的流式数据

5.png

GPS 物联网 银行数据

1.2 传统SQL和流处理

特征SQL流处理
处理数据的有界性处理的表是有界的流是一个无限元组序列
处理数据的完整性执行查询可以访问完整的数据执行查询无法访问所有的数据
执行时间产生固定大小结果后终止不断更新结果,永不终止

问题:如何定义流处理中的表呢?有什么特点呢?

1.3 概述-数据流和动态表转换 - 重点

3.png

从上图我们要有一个认识:表和流是可以动态转换的

下文将介绍各个部分。

在流上定义表

下图显示了单击事件流如何转换为表。当插入更多的单击记录时,结果表将不断增长。

1c78e8d1-4fc3-480e-8e09-ef879a25d97c.png

  • 动态表 Dynamic Table 随时间不断变化的表,在任意时刻,可以像查询静态批处理表一样查询它们

1.4 连续查询

动态表-->新动态表

  • 查询不会终止
  • 查询结果会不断更新,产生一个新的动态表

1.5 查询产生仅追加数据的动态表

没有重叠的窗口

时间窗口不一样,不会更新之前的记录

1.6 两个连续查询对比

1.png

2.png

  • 第一个查询更新先前输出的结果,即定义结果表的changelog 流包含INSERT 和UPDATE 操作;
  • 第二个查询只附加到结果表,即changelog流只包含 INSERT 操作。

1.7 Retract 消息的产生

动态表到实时流的转换

  • Append-only Stream: Append-only 流(只有 INSERT 消息)
  • Retract Stream: Retract 流(同时包含 INSERT 消息和 DELETE 消息)

6.png 回撤消息,对之前结果进行更新

当表中第三行再次出现Mary时,对Mary这条消息进行回撤(delete),并更新cnt值,由1变为2

1.8 状态 state

在流式计算中,会存在有状态的计算逻辑(算子)

比如,需要计算某个用户在网上的点击量,该用户在网站当前的总点击次数就是算子状态,对于新的输入数据,先判断是否是该用户的点击行为,如果是,则将保留的点击次数(状态)增加一,并将当前累加结果输出。

1.9 回顾

3.png

  • 数据流和动态表之间的转换
  • 在数据流的查询不会终止
  • 查询可能会有状态,用来不断更新查询的结果

1.10 不同数据处理保证的语义

  • At-most-once:每条数据消费至多一次,处理延迟低。 出现故障,什么也不做

  • At-least-once:每条数据消费至少一次,一条数据可能存在重复消费

  • Exactly-once:最严格的处理语义。每条数据都被消费且仅被消费一次,仿佛故障从未发生

02. Exactly-Once 和 Checkpoint

2.1 状态快照与恢复

例子:对输入流分别计算奇数和与偶数和

7.png

保存当前的状态,当发生错误时,可以从该状态重新开始执行

2.2 制作快照的时间点

一个简单的快照制作算法:

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

2.3 Chandy-Lamport 算法

  • 快照制作的开始

    每一个source算子都接收到 JM 发送的Checkpoint Barrier 标识状态快照制作的开始

    8.png

  • Source 算子的处理

    各个 source 保存自己的状态后, 向所有连接的下游继续发送 Checkpoint Barrier,同时告知 JM自己状态以及制作完成

    9.png

  • Barrier Alignment

    • 算子会等待所有上游的barrier 到达后才开始快照的制作

    • 已经制作完成的上游算子会继续处理数据,并不会被下游算子制作快照的过程阻塞

    • 说明:这里Sum even算子中,数据’4‘虽然已经到达,但仍有barrier未到达,所以暂时还不进行数据处理

      10.png

  • 快照制作和处理数据的解耦

  • Checkpoint 的结束 所有算子都告知 JM 状态制作完成后,整个 Checkpoint就结束了。

2.4 Checkpoint 对作业性能的影响

  • 解偶了快照制作和数据处理过程,各个算子制作完成状态快照后就可以正常处理数据,不用等下游算子制作完成快照

  • 在快照制作和 Barrier Alignment 过程中需要暂停数据处理,仍然会增加数据处理延迟

  • 快照保存到远端也有可能极为耗时

03. 端到端 Exactly-Once 实现

3.1 端到端 Exactly-Once 语义

  • Checkpoint 能保证每条数据都对各个有状态的算子更新一次, sink输出算子仍可能下发重复的数据
  • 严格意义的端到端的 Exactly-Once 语义需要特殊的 sink 算子实现
  • eg. 保证 A账户-500 B账户+500

3.2 两阶段提交协议

  • Coordinator:协作者,同步和协调所有节点处理逻辑的中心节点

  • Participant:参与者,被中心节点调度的其他执行处理逻辑的业务节点

    4.png

(一)预提交阶段

  1. 协作者向参与者发送commit消息
  2. 协作者执行事务,但不是真正提交
  3. 若成功,发送成功的消息 vote yes,失败 vote no

(二)提交阶段

  • 若协作者成功接收所有参与者vote yes 的消息
  1. 协作者向参与者发送commit消息
  2. 收到commit的参与者释放执行事务所需要的资源,并结束这次事务的执行
  3. 发送ack消息
  4. 协作者收到ack消息,标识该事务执行成功
  • 若收到vote no消息:
  1. 协作者向所有参与者发送 rollback 消息
  2. 参与者回滚事务的执行操作,并释放占用资源
  3. 发送ack消息
  4. 协作者收到ack消息,标识该事务成功完成回滚

3.3 两阶段提交协议在 Flink 中的应用

  • Flink 中协作者和参与者的角色分配

    11.PNG

    4.png

  • 协作者(JobManager)发起阶段一提交

    12.png

  • 各算子 Checkpoint 的制作

    13.png

  • 提交阶段及 Checkpoint 的制作完成

    14.png

参考:Apache Flink: An Overview of End-to-End Exactly-Once Processing in Apache Flink (with Apache Kafka, too!)

04. Flink案例讲解

4.1 账单计算服务

场景简介

​ 从kafka中读取账单消息,进行处理后写入到MySQL中

15.png

当前方案

  1. 在上次记录的位点之后,从kafka中读取固定大小的数据
  2. 对该批数据进行去重和聚合计算
  3. 处理完成后写入MySQL中,若全部写入成功,则记录当前读取到的消息的终止位置;若处理或写入失败,则不记录位点
  4. 跳回步骤一

存在的问题

  1. 非严格意义上的端到端的 Exactly-Once 语义:若该批数据处理完后,在写入MySQL中发生异常,则存在部分数据写入的情况,下次作业启动,这部分数据仍然会重复写入;
  2. 去重能力有限:只能在当前处理的一批数据内去重,无法在批与批之间进行去重;

Flink方案

  • 优势:
  1. 严格意义上的端到端的 Exactly-Once 语义:下游读到的数据是不丢不重的;

  2. 增强的去重能力:可以在更长的时间维度对数据进行去重

总结

  • 数据流可以转换成动态表,动态表也可以重新转换成数据流
  • 处理无限数据流的算子可以是有状态
  • Flink通过 CheckPoint 机制实现故障前后的状态快照制作和恢复
  • 支持两阶段提交协议的下游存储可以结合Flink Checkpoint 机制实现严格意义上的端到端的 Exactly-Once 语义实现