Excatly Once语义在Flink的实现||青训营
这是我参加第四届青训营的的第三天
一、数据流和动态表(如何在数据流上执行SQL语句说明流式处理中状态的概念)
1.1随处可见的流式数据
示例:银行 GPS等;
1.2传统SQL和流处理
特征 SQL 流处理
处理数据的有界性 处理的表是有限的 流是一个无限元祖序列
处理数据的完整性 执行查询可以访问完整的数据 执行查询无法访问所有点数据
执行时间 批处理查询产生固定大小结果后终止 查询不断更新结果,永不终止
1.3概述-数据流和动态表转换
`源源不断的数据流 转换为动态表 然后执行sql查询 然后table转换表`
1.3在流上定义表
将用户登陆时间姓名地址,不断转换 动态表:与表示批处理数据的静态表不同,动态表示随时间变化的,可以像静态批处理表一样查询它们,不会因为查询而终止,没点击一次就会更改动态表的数值,等着bob的数据输入
1.4连续查询
连续查询:
查询从不终止
查询结果会不断更新,产生一个新的动态表 动态表————>动态表
1.5查询产生仅追加数据的动态表
统计用户不重复新增加的一个小时之内的数据
1.6两个连续查询对比
第一个只会更新先前输出的结果,即定义结果表的changelog流包含insert和upupdate操作;
第二个只附加到结果表,即结果表的changelog只包含insert操作
1.7 Retract消息的产生
先发——号回撤消息然后发更新后的结果,只会在流式里面出现,因为需要不挺更新
1.8 状态
需要存储每个用户的URL计数,以便能够增加该计数并在输入表接受新行时发送新结果
1.9数据流和动态表转换回顾
1.数据流和动态表的转换
2.在数据流的查询不会终止
3.查询可能会有状态,用来不断更新查询的结果
1.10不同数据处理保证的语义
1.At-most-once:出现故障的时候,啥也不做。数据处理不保证任何语义,处理时延低;
2.At-least-once:保证每条数据均至少被处理一次,一条数据可能存在重复消费。
3.Exactly-once:最严格的处理语句,从输出结果来看,每条数据均被消费且仅消费一次,仿佛故障从未发生。
二、Exactly-ONce和checkpoint(当故障发生时,flink自身如何从故障中恢复,保证对数据消费的不丢不重)
2.1状态快照与恢复
不大的数据流使用偶数进行累加,大的使用奇数进行累加,在累加的过程中需要记录,如果故障会拿最近的一个状态进行恢复,这样可以做到数据不多不重;
2.2制作快照的时间点
状态恢复的时间点:需要等待所有处理逻辑消费完成source保留及之前的数据。
一个简单的快照制作算法:
1.暂停处理输入的数据
2.等待后续所有处理算子消费当前已经输入的数据
3.待2处理完后,作业所以算子复制自己的状态并保存到远端可靠存储
4.恢复对输入数据的处理
2.3 chandy——Lamport算法
并行独立的处理各种数据
2.3.1快照制作的开始
每一个source算子都接受到JM发送的checkpointBarrier标识状态已经制作完成。
2.2.2Source算子的处理
各个source保存自己状态后,向所有连接的下游继续发送Checkpoint Barrier,同时告知JM自己状态已经制作完成,不需要等待所有点状态的完成。
2.2.3Barrier Alignment
如果sour1到了后面的4也到了,我的所有sour都到了之后才会处理,必须所有都到才会恢复过程
1.算子会等待所有上游的barrier到达后才开始快照的制作;
2.已经制作完成的上游算子会继续处理数据,并不会被下游算子制作的过程阻塞
2.3.4快照制作和处理数据的解耦
2.3.5checkpoint的结束
所有算子都告知JM状态制作完成后,整个checkpoint就结束了。
2.4Checkpoint对作业性能的影响
1.解耦了快照制作和数据处理过程,各个算子制作完成状态快照后就可以正常处理数据,不要等下游算子制作制作完成快照
2.在快照制作和Barrier Alignment过程中需要暂停处理数据,仍然会增加数据处理延迟;
3.快照保存到远端也有可能极为耗时。
三、端到端exactly-Once实现(flink本身的Checkpoint机制如何和外部存储结合,实现端到端的不丢不重语义)
3.1端到端 Exactly-once语义
1.Checkpoint能保证每条数据都对各个有状态的算子更新一次,sink输出算子仍有可能下发重复的数据;
2.严格意义的端到端Exactly-on测语义需要特殊的sink算子实现。
3.2两阶段提交协议
在多个节点参加执行的分布式系统,为了协调每个节点都能同时执行或者回滚某个事务性(一系列的操作,要不全部执行要不全部不执行)的操作,引入了一个中心节点来统一处理所有节点的执行逻辑,这个中心节点叫做执行者(coordinator),被中心节点调度的其他业务节点叫做参与者(participant)。
3.2.1两阶段提交协议(一)-预提交阶段
`1.协作者向所有参与者发送一个commit消息;
2.每个参与协作者收到信息执行事务,但是不真正提交;
3.若事务成功执行完成,发送一个成功的消息(vote yes);执行失败,则发送一个失败的消 息(vote no);`
3.2.2两阶段提交协议(二)-提交阶段
若协作者成功接收到所有的参与者vote yes的消息:
1.协作者向所有参加者发送一个commit消息;
2.每个收到commit消息的参与者释放执行事务所需的资源,并结束这次事务的执行;
3.完成步骤2后,参与者发送一个ack消息给协作者;
4.协作者收到所有参与者的ack消息后,标识该事务执行完成。
3.2.2两阶段提交协议(二)-提交阶段
#### 若协作者有接收到参与者vote yes的消息(或发生等待超时):
1.协作者向所有参加者发送一个rollback消息;
2.每个收到rollback消息的参与者回滚事务的执行操作,并释放事务所所占资源;
3.完成步骤2后,参与者发送一个ack消息给协作者;
4.协作者收到所有参与者的ack消息后,标识该事务成功完成回滚。
3.3Flink中2PC Sink
协作者:管理所有的协议;
参与者:协作所有的协议;
Kafka:二维数组,每个数组是无限长插入数据从后面插,一般从头查看
Source window datasink:统计计算节点
state:把结果传送到这里面;
##### job发送标准着状态开始,data发送state分发所有,开启制作,成功发送成功的消息,反之发送失败,成功后sink发送给kafka代表成功;只有发布成功才是可见的;
3.4Flink两阶段提交总结
1.事务开启:在sink task向下游写数据之前,均会开启下一个事务,后续所有写数据的操作均在这个事务中执行,事务未提交前,事务写入的数据下游不可读;
2.预提交阶段:jobmanager开始下发checkpoint Barrier,当各个处理逻辑接受到barrier后停止处理后续数据,对当前状态制作快照,此时sink也不在当前事务下继续处理数据(处理后续的数据需要重新打开下一个事务)。状态制作成功则向JM成功的消息,失败则发送失败的消息;
3.提交状态:若JM收到所有预处理提交成功的信息,则向所有处理逻辑(包括sink)发送可以提交此次事务的消息,sink接受到此消息后,则完成此次事务的提交,此时下游可以读到这次事务写入的数据,若JM有收到预提交失败的消息,则通知所有出来逻辑回滚这次事务的操作,此时sink则丢弃这次事务提交的数据下。
四、Flink案例讲解(选择字节内部真实的案例场景,介绍flink如何解决和实现账单计费服务)
4.1账单计算服务:场景简介
从kafak中读取账单消息,进行处理后写入到MySQL中
### 执行步骤:
1.在上次记录的位点之后,从kafak中读取固定大小的数据;
2.对该批数据进行去重和聚合计算
3.处理完成后写入mysql中,若全部写入成功,则记录下当前读取到的消息的终止位置;若处理或者写入失败,则不记录位点;
4.跳回步骤1;
### 存在的问题:
1.非严格意义上的端到端的Exactly-once语义:若该批数据处理完成后,在写入MySQL中发生异常,则存在部分数据写入的情况,下次作业启动后,这部分仍然会重复写入;
2.去重能力有限:只能在当前处理的一批数据内进行去重,无法在批与批之间进行去重