这是我参与「第四届青训营 」笔记创作活动的第2天
Flink 将传统数据库表的概念扩展到了数据流。
为了实现这个目标,业界提出的称呼是动态表。传统的数据库表相当于是一个列表,总归是有穷的、确定的,而数据流则是无穷的,类似Java中的无限流Stream,Python中无限生成器Generator。
这里其实有一个隐含的意思,这种查询主要是指聚合操作类型,而不是普通意义上的查询。举个例子,比如说求和、分组等等。
以基于时间发生时间的数据流为例,做一个比较好运的假设,数据流的时间戳是按照正确顺序的,可以有比较经典的一些查询模型。
第一种是对时间戳不管不顾,做类似传统 word count 模型,好处是有了截止到目前的统计信息,可以算是有实时性,只是缺少了时间段的变化,使用场景有限。
第二种则是对时间戳进行分组,或者是进一步切割时间段,基于此进一步更有区分性的word count计算,这也是目前主流的流计算方式。 无论是第一种还是第二种,涉及的最大问题都是如何保障计算的准确性,不重不漏。这也是流计算中重要的 “Exactly Once” 概念。 实际上说起来也不是那么复杂,重点就是要保障传入的每条数据被准确的计算了一次,并且将每个计算的结果准确无误的写入或者是更新。 在Flink这些流计算的框架中,Exactly Once 机制的保障分为了三个部分的措施:source,sink以及中间的计算部分。
对于source部分来说,最重要的是可以得到每一条数据,让source可以为计算部分提供完整的数据流用于计算。这要求来源的数据可以被再次请求,也就是所谓的重放机制,并且额外这里能保证幂等性,也就是能够区分数据之间的不同。一般这里接入的是消息队列,只需要at least once的语言即可。计算层得到这一层提供的完整不重复的数据就用于计算即可。
对于计算部分来说,在不出错的情况下,可以保证数据只被计算一次,并且Flink甚至Spark都有检查点技术,配合上source提供的保障,可以在真的出错的情况下,更快的恢复过来。
最麻烦的在于sink端,flink由于自身故意的处理机制,因此可以表现出数据的不重不漏,同样的sink如果希望继续维持这种情况,也需要有一些保障机制。如果仅仅每个部分只有一个节点那倒是简单,只需要单纯的事务机制就可以了。然而实际的环境中,必然需要相当多的节点保障整体的可靠性,这样一来不同的节点同时完成事务就需要所谓的分布式事务了,flink中使用的是最常见的办法 2PC 两阶段提交协议。
简单说的话就是节点之间需要引入一个协调者,保障所有的节点都完成了数据写入和事务提交。