时间和窗口
乱序流中的水位线
每隔一段时间,生成水位线=最大时间戳-延迟时间(2)
水位线向下游广播,确保下游窗口时间同步。
下游收到的来自上游的不同水位线,存储所有分区的水位线,取最小一个作为自己水位线。
所以 Flink 中的水位线,其实是流处理中对低延迟和结果正确性的一个权衡机制,而且把控制的权力交给了程序员,我们可以在代码中定义水位线的生成策略。其思想继承lamport时钟,功能上完成了exactly one。
窗口
将流处理转化为微型批处理,以提升效率
(1)第一个数据时间戳为 2,判断之后创建第一个窗口[0, 10),并将 2 秒数据保存进去;
(2)后续数据依次到来,时间戳均在 [0, 10)范围内,所以全部保存进第一个窗口;
(3)11 秒数据到来,判断它不属于[0, 10)窗口,所以创建第二个窗口[10, 20),并将 11秒的数据保存进去。由于水位线设置延迟时间为 2 秒,所以现在的时钟是 9 秒,第一个窗口也没有到关闭时间;
(4)之后又有 9 秒数据到来,同样进入[0, 10)窗口中;
(5)12 秒数据到来,判断属于[10, 20)窗口,保存进去。这时产生的水位线推进到了 10秒,所以 [0, 10)窗口应该关闭了。第一个窗口收集到了所有的 7 个数据,进行处理计算后输出结果,并将窗口关闭销毁;
(6)同样的,之后的数据依次进入第二个窗口,遇到 20 秒的数据时会创建第三个窗口[20, 30)并将数据保存进去;遇到 22 秒数据时,水位线达到了 20 秒,第二个窗口触发计算,输出结果并关闭。
驱动类型
时间窗口:[begin,end)
计数窗口:到达计数窗口关闭
分配规则
滚动窗口:窗口间无重叠
滑动窗口:每次滑动,有一个滑动步长
会话窗口:间隔多长时间,没有数据则切断会话,重新建立窗口
全局窗口:
检查点
在不暂停整体流处理的前提下,将状态备份保存到检查点。在Flink中,采用了基于Chandy-Lamport算法的分布式快照。即让每个source认出哪一个数据是检查点对应数据(处理完该数据后保存检查点)
方法:借鉴水位线(watermark)的设计,在数据流中插入一个特殊的数据结构,专门用来表示触发检查点保存的时间点。收到保存检查点的指令后,Source 任务可以在当前数据流中插入这个结构;之后的所有任务只要遇到它就开始对状态做持久化快照保存。由于数据流是保持顺序依次处理的,因此遇到这个标识就代表之前的数据都处理完了,可以保存一个检查点;而在它之后的数据,引起的状态改变就不会体现在这个检查点中,而需要保存到下一个检查点。(乱序,网络延迟不会影响checkpoint吗?)
“异步分界线快照”(asynchronous barrier snapshotting)算法当上游任务向多个并行下游任务发送 barrier 时,需要广播出去;而当多个上游任务向同一个下游任务传递 barrier 时,需要在下游任务执行“分界线对齐”(barrier alignment)操作,也就是需要等到所有并行分区的 barrier 都到齐,才可以开始状态的保存。(那部分数据被保存到了上一个checkpoint?)
端到端的一致性
入口端的一致性由source源支持,如Kafka就支持回溯到以前的某个offset,而socket就不行
出口的一致性flink有两种方式解决
幂等性
无论写出多少次,结果只改变一次(但是在flink回溯时,会与出口端出现短暂的不一致)
事务
保证写出的事务性质,即ACID。让写入操作可以随着检查点来提交和回滚,所以事务是与检查点绑定在一起的。
实现方式:
预写日志(WAL)
①先把结果数据作为日志(log)状态保存起来
②进行检查点保存时,也会将这些结果数据一并做持久化存储(log)
③在收到检查点完成的通知时,将所有结果一次性写入外部系统。
模板类GenericWriteAheadSink提供API
两阶段提交(2PC)
①当第一条数据到来时,或者收到检查点的分界线时,Sink 任务都会启动一个事务。
②接下来接收到的所有数据,都通过这个事务写入外部系统;这时由于事务没有提交,所以数据尽管写入了外部系统,但是不可用,是“预提交”的状态。
③当 Sink 任务收到 JobManager 发来检查点完成的通知时,正式提交事务,写入的结果就真正可用了。
此方法仍然需要外部系统一定程度上的事务支持
1.外部系统必须提供事务支持,或者 Sink 任务必须能够模拟外部系统上的事务。
2.在检查点的间隔期间里,必须能够开启一个事务并接受数据写入。
3.在收到检查点完成的通知之前,事务必须是“等待提交”的状态。在故障恢复的情况下,这可能需要一些时间。如果这个时候外部系统关闭事务(例如超时了),那么未提交的数据就会丢失。
4.Sink 任务必须能够在进程失败后恢复事务。
5.提交事务必须是幂等操作。也就是说,事务的重复提交应该是无效的。
TwoPhaseCommitSinkFunction提供API
实践中还是经常选择Kafka作为出口,官方sink内置了两阶段提交
尚硅谷大数据技术之Flink1.13(Java)