这是我参与「第四届青训营 」笔记创作活动的第4天
1.概述
简述流式计算的基本概念,与批式计算相比的难点和挑战
①流式计算vs批式计算
数据价值:实时性更高,数据价值更高
②批处理
批处理模型典型的数仓架构是T+1结构,即数据计算是天级别的,当天只能看到前一天的计算结果,通常使用的计算引擎为Hive或者Spark等。计算的时候,数据是完全ready的,输入和输出都是确定性的
小时级批计算
- 批处理除了计算外,还存在资源申请、释放的周期调度的过程,耗时,未必能在一小时内完成
③处理时间窗口
实时计算:处理时间窗口
数据实时流动,实时计算,窗口结束直接发送结果,不需要周期调度任务
④处理时间vs事件时间
处理时间:数据在流式计算系统中真正处理时所在机器的当前时间
事件时间:数据产生的时间,比如客户端、传感器、后端代码等上报数据时的时间
⑤事件时间窗口
实时计算:事件时间窗口
数据实时进入到真实事件发生的窗口中进行计算,可以有效的处理数据延迟和乱序
问题:什么时候窗口才算结束
⑥Watermark
在数据中插入一些watermark,来表示当前的真实时间
在数据存在乱序的时候,watermark就比较重要了,它可以用来在乱序容忍和实时性之间做一个平衡
⑦小结
- 批式计算一般是T+1数仓架构
- 数据实时性越高,数据的价值越高
- 实时计算分为处理时间和事件时间
- 事件时间需要Watermark配合来处理乱序
2.Watermark
Watermark的含义、生成方法、传递机制,以及一些典型场景的问题和优化
①Watermark含义、生成方法
表示系统认为的当前真实的事件时间
生成watermark
②如何传递Watermark
拓扑中的每个节点取上游传递来的所有watermark中数值最小的,并向下游的所有节点传递其watermark数值
③如何通过Flink UI观察Watermark
④典型问题
1.Per-partition VS per-subtask watermark生成
-
Per-partition watermark生成
早期版本都是这种机制。典型的问题是如果一个source subtask消费多个partition,那么多个partition之间的数据读取可能会家具乱序程度
-
Per-subtask watermark生成
新版本引入了基于每个partition单独的watermark生成机制,这种机制可以有效避免上面的问题
2.部分partition/subtask断流
- 根据上面提到的watermark传递机制,下游subtask会将上游所有subtask的watermark值的最小值作为自身的watermark值。如果上游有一个subtask的watermark不更新了,则下游的watermark都不更新
- 解决方案:Idle source——当某个subtask断流超过配置的idle超时时间时,将当前subtask置为idle,并下发一个idle的状态给下游。下游在计算自身的watermark时,可以忽略掉当前是idle的那些subtask
3.迟到数据处理
-
因为watermark表示当前事件发生的真实时间,那晚于watermark的数据到来时,系统会认为这种数据是迟到的数据
-
算子自身来决定如何处理迟到数据
Window聚合,默认会丢弃迟到数据
双流join,如果是outer join,则可以认为它不能join到任何数据
CEP(复杂事件处理),默认丢弃
⑤小结
- 含义:表示系统认为的当前真实时间
- 生成:可以通过Watermark Generator来生成
- 传递:取上游所有subtask最小值
- 部分数据断流:Idle Source
- 吃到数据处理:Window算子是丢弃;join算子则认为跟之前的数据无法join到
3.Window
Window基本功能和高级优化
①基本功能
1.Window分类
-
典型的Window
Tumble Window(滚动窗口)
-
数据时间唯一确定窗口,窗口大小不变,只是过了时间间隔后窗口向后滚动
-
窗口划分
每个key单独划分
每条数据只会属于一个窗口
-
窗口触发
Window结束时间到达的时候一次性触发
Sliding Window(滑动窗口)
-
数据时间唯一确定窗口
-
窗口划分
每个key单独划分
每条数据可能会属于多个窗口
-
窗口触发
Window结束时间到达的时候一次性触发
Session Window(会话窗口)
-
窗口划分是一个动态的过程
-
窗口划分
每个key单独划分
每条数据会单独划分为一个窗口,如果window之间有交集,则会对窗口进行merge
-
窗口触发
Window结束时间到达的时候一次性触发
-
-
其他Window
全局Window
Count Window
累计窗口
2.窗口的使用
3.迟到数据处理
-
如何定义迟到
一个数据到来后,会用WindowAssigner给它划分一个window,一般时间窗口是一个时间区间,如果划分出来的window end比当前的watermark值还小,说明这个窗口已经触发计算了,这条数据会被认为是迟到数据
-
什么情况下会产生迟到数据
只有事件时间下才会有迟到的数据
-
迟到数据默认处理
丢弃
-
其他处理方式
-
Allow lateness
这种方式需要设置一个允许迟到的时间,设置之后,窗口正常计算结束后,不会马上清理状态,而是会多保留allowLateness这么长时间,在这段时间内如果还有数据到来,则继续之前的状态进行计算
适用于:DataStream、SQL
-
SideOutput(侧输出流)
这种方式需要对迟到数据打一个tag,然后在DataStream上根据这个tag获取迟到数据流,然后业务层面自行选择进行处理
适用于:DataStream
-
4.增量VS全量计算
-
增量计算
- 每条数据到来,直接进行计算,window只存储计算结果。比如计算sum,状态中只需要存储sum的结果,不需要保存每条数据(每次数据处理的覆盖使用retract机制)
- 典型的reduce、aggregate等函数都是增量计算
- SQL中的聚合只有增量计算
-
全量计算
- 每条数据到来,会存储到window的state中。等到window触发计算时,将所有数据拿出来一起计算
- 典型的process函数就是全量计算
5.EMIT触发
-
什么是EMIT
通常来讲,window都是在结束的时候才能输出结果,比如1h的tumble window,只有在一个小时结束的时候才能统一输出结果
如果窗口比较大,比如1h或者1天,甚至于更大的话,那计算结果输出的延迟就比较高,失去了实时计算的意义
EMIT输出指的是:在window没有结束的时候,提前把window计算的部分结果输出出来(同样用到了retract机制)
-
如何实现
在Datastream里面可以通过自定义Trigger来实现,Trigger的结果可以是:CONTINUE、FIRE(触发计算,但是不清理)、PURGE、FIRE_AND_PURGE
-
SQL也可以使用,通过配置
table.exec.emit-fire.enabled=true
table.exec.emit-fire.delay={time}
②高级优化
-
Mini-batch优化
(增量计算)正常情况下,每来一条数据都会产生一个retract流
状态分为:放在内存的状态和放在外部的状态
读状态、更新状态、写回状态,访问状态非常频繁,每一次访问都需要做序列化和反序列化,这样对CPU开销很大
- 优化:先攒一批数据,然后集中处理,减少对状态的访问,但是会有延迟,可以借用watermark的机制,上游向下游发送mini-batch标识
-
倾斜优化——local-global
不同key的数据量可能会相差很大,因此在将上游数据发送给下游前,可以先做一次聚合(local),再发给下游(global)
-
Distinct计算状态复用
数据库系统或者批计算对distinct的处理都是先做group by再count,而在流处理中若这样处理会让窗口难以处理,因此可以将distinct信息保存到状态中,如果每一个count distinct都需要一个状态,那么状态量会非常大
若如图所示,那么可以让一个指标的多个FILTER条件复用一个状态,bit value表示不同的filter条件下,对应的key是否来过
-
Pane优化
滑动窗口中,单条数据可能会属于多个窗口,若滑动时间在窗口大小所占比例过小(一天的窗口,1h的滑动),那么每条数据会被重复处理多次
将一个窗口进行划分,使得每条数据近似属于一个窗口,但是实际上仍是(如例)三个连续的pane属于一个窗口,但是最后在窗口输出结果时需要进行一个merge(只是在计算和存储方面进行优化)
-
小结
- Mini-batch优化解决频繁访问状态的问题
- local-global优化解决倾斜问题
- Distinct状态复用降低状态量
- Pane优化降低滑动窗口的状态存储量
4.案例分析
抖音DAU实时曲线计算,大数据任务资源使用实时统计分析
①需求一:使用Flink SQL计算抖音的日活曲线
做一个滚动窗口,然后使用EMIT机制,让窗口提前输出
问题:所有的数据都需要在一个subtask中完成窗口计算(最后的聚合),无法并行
改进:通过两阶段聚合来把数据打散,完成第一轮聚合,第二轮聚合只需要对各个分桶的结果求和即可(相当于倾斜优化)
②需求二:使用Flink SQL计算大数据任务的资源使用
典型的可以通过会话窗口来将数据划分到一个Window中,然后再将数据求和即可
5.小结
- 流式计算基本概念,以及和批式计算的区别
- watermark的含义、如何生成、如何传递,以及如何处理部分partition断流的问题
- 介绍了三种基本的window的定义,以及迟到数据处理、增量计算VS全量计算,EMIT输出;同时介绍了local-global优化,mini-batch优化,distinct状态优化,滑动窗口的pane优化等
- 两个案例介绍滚动窗口、会话窗口,以及两阶段聚合解决倾斜问题