流式计算中的 Window 计算
这是我参与「第四届青训营 」笔记创作活动的第5天。
上节回顾: 流式计算的动态表 Flink中的State和Checkpoint的基本原理 Flink的Retract机制,以及算子如何产生和处理Retract数据 Flink中如何实现Exactly-Once的语义
视频课程内容补充:
- 实时计算和批使计算的本质区别,以及实时计算所带来的新的挑战和机遇
- 了解实时计算中的核心功能:Watermark机制,Window机制
- 了解3大基本窗口类型的定义,使用以及核心原理
- 了解窗口机制中最核心的优化及其原理
本课程主要分为四个部分:
-
概述流式计算跟批计算,以及实时数仓和离线数仓的区别;引出流式计算中的window计算定义以及挑战
-
介绍实时计算中的Watermark概念,以及如何产生、传递,还有一些典型的生产实践中遇到的问题
-
介绍三种最基本的window类型,以及他们的实现原理;同时会结合业务场景介绍一些高级优化的功能和原理
-
结合两个真实业务场景(抖音DAU实时曲线计算和大数据任务资源使用实时统计分析)的需求,讲解window是如何解决实际生产问题的
1.流式计算vs批式计算
批处理
典型的数仓架构是T+1架构,技术局计算时天级别的,当天只能看到前一天的计算结果。 通常使用的计算引擎是Hive或者Spark等。计算时,数据时完全ready的,输入域输出都是确定的。
把处理时间缩短为每小时处理,是否可以称为实时数仓?
于是有小时级批计算
比小时级批计算更实时的
处理时间窗口
数据实时流动,实时计算,窗口结束直接发送结果,不需要周期调度任务。
处理时间vs事件时间
处理时间:数据在流式计算系统中真正处理时所在机器的当前时间。
事件时间:数据产生的时间,比如客户端,传感器等上传数据时的时间。
有一些数据会延迟,想表达实时处理中事件时间的过程中,怎么表达系统中当前衡量的真实的数据的时间
Watermark定义:当前系统认为的事件时间所在的真实时间
w(11)表示,我们确定11该时间戳之后的时间序列都大于之前左后一个的时间序列号,如果之后有小于之前最后一个时间序列号的时间序列被认为是延迟的,需要特殊执行。
Watermark
Watermark定义:当前系统认为的事件时间所在的真实时间。
Watermark产生:一般是从数据的事件时间来产生,产生策略可以灵活多样,最常见的包括使用当前事件时间的时间减去一个固定的delay,来表示可以可以容忍多长时间的乱序。
取上游所有的Watermark值的最小值
通过Flink UI 观察Watermark
在生产实践中Watermark出现的问题:
典型问题:
介绍典型的watermark在生产实践中经常遇到的几个问题:
-
怎么观察一个任务中的watermark是多少,是否是正常的
- 一般通过Flink Web UI上的信息来观察当前任务的watermark情况
- 这个问题是生产实践中最容易遇到的问题,大家在开发事件时间的窗口任务的时候,经常会忘记了设置watermark,或者数据太少,watermark没有及时的更新,导致窗口一直不能触发。
-
Per-partition / Per-subtask 生成watermark的优缺点
- 在Flink里早期都是per-subtask的方式进行watermark的生成,这种方式比较简单。但是如果每个source task如果有消费多个partition的情况的话,那多个partition之间的数据可能会因为消费的速度不同而最终导致数据的乱序程度增加。
- 后期(上面图中)就逐步的变成了per-partition的方式来产生watermark,来避免上面的问题。
-
如果有部分partition/subtask会断流,应该如何处理
- 数据断流是很常见的问题,有时候是业务数据本身就有这种特点,比如白天有数据,晚上没有数据。在这种情况下,watermark默认是不会更新的,因为它要取上游subtask发来的watermark中的最小值。此时我们可以用一种IDLE状态来标记这种subtask,被标记为这种状态的subtask,我们在计算watermark的时候,可以把它先排除在外。这样就可以保证有部分partition断流的时候,watermark仍然可以继续更新。
-
算子对于时间晚于watermark的数据的处理
- 对于迟到数据,不同的算子对于这种情况的处理可以有不同的实现(主要是根据算子本身的语义来决定的)
- 比如window对于迟到的数据,默认就是丢弃;比如双流join,对于迟到数据,可以认为是无法与之前正常数据join上。
Watermark小结
- 1.含义:表示系统认为的当前真实时间
- 2.生成L可以通过Water Generator 生成
- 3.传递:取上游所有subtask的最小值
- 4.部分数据断流:Idle Source
- 5.迟到数据处理:Window 算子是丢弃;join算子任务跟之前的数据无法join到
Window分类
典型的Window:
- 1.Tumble Window(滚动窗口)
- 2.Sliding Window(滑动窗口)
- 3.Session Window(会话窗口)
其他窗口:
- 全局Window
- 累计窗口
- ...
右侧是Flink的api的分层结构
SQL层和Table API层不是调用关系,他们的设计实现是独立的。
TUMBLE Window (滚动窗口)
- 窗口的定义:根据时间和key划分窗口,每条数据只属于一个窗口
- 窗口输出:只有到窗口结束,才会一次性把结果发送出去
这是最常见的窗口类型,就是根据数据的时间(可以是处理时间,也可以是事件时间)划分到它所属的窗口中windowStart = timestamp - timestamp % windowSize,这条数据所属的window就是[windowStart, windowStart + windowSize)
在我们使用window的过程中,最容易产生的一个疑问是,window的划分是subtask级别的,还是key级别的。这里大家要记住,Flink 中的窗口划分是key级别的。 比如下方的图中,有三个key,那每个key的窗口都是单独的。所以整个图中,一种存在14个窗口。
窗口的触发,是时间大于等于window end的时候,触发对应的window的输出(计算有可能提前就增量计算好了),目前的实现是给每个window都注册一个timer,通过处理时间或者事件时间的timer来触发window的输出。
HOP Window (滑动窗口)
- 窗口的定义:根据时间和key划分窗口,每条数据可能会属于多个窗口
- 窗口输出:只有到窗口结束,才会一次性把结果发送出去
在HOP窗口中,每条数据是可能会属于多个窗口的(具体属于多少,取决于窗口定义的大小和滑动),比如上图中假设滑动是1h的话,那窗口大小就是2h,这种情况每条数据会属于两个窗口。除了这一点之外,其它的基本跟HOP窗口是类似的,比如也是key级别划分窗口,也是靠timer进行窗口触发输出。
SESSION Window (会话窗口)
- 窗口的定义:根据时间和key划分窗口,每条数据会单独划分为一个窗口,如果window之间有交集,则会对窗口进行merge
- 窗口输出:只有到窗口结束,才会一次性把结果发送出去
会话窗口的话,是一个动态merge的过程。一般会设置一个会话的最大的gap,比如10min。 那某个key下面来第一条数据的时候,它的window就是 [event_time, event_time + gap),当这个key后面来了另一条数据的时候,它会立即产生一个窗口,如果这个窗口跟之前的窗口有overlap的话,则会将两个窗口进行一个merge,变成一个更大的窗口,此时需要将之前定义的timer取消,再注册一个新的timer。
所以会话窗口要求所有的聚合函数都必须有实现merge。
窗口机制中最核心的优化及其原理
使用窗口机制我们产生的一些典型问题:
迟到数据处理
怎么定义迟到?
- 一条数据到来后,会用WindowAssign给它划分一个Window,一般时间串口是一个时间区间,一般时间晚于Watermark我们定义为迟到,在窗口机制中,我们的定义宽松一些,如果一条数据它晚于Watermark,但没有晚于window end,我们依然认为他没有迟到。
什么情况会出现吃到数据?
- 只有事件时间下才会有迟到的数据
迟到数据默认处理?
- 默认丢弃
迟到数据的处理
1.Allow lateness
这种方式需要设置一个允许迟到的时间。设置后,窗口正常计算结束后,不会马上清理状态,而是多保留allowlasteness这么长时间,这段时间内如果还有数据来,则继续之前的状态进行计算。
2.SideOutput(侧输出流)
把迟到的数据转打一个tag,然后在DataStream上根据这个tag获取吃到数据流,然后业务层面自信选择进行处理。
增量计算 VS 全量计算
这个问题也是使用窗口的时候最典型的问题之一。先定义一下:
- 增量计算:每条数据到来后,直接参与计算(但是还不需要输出结果)
- 全量计算:每条数据到来后,先放到一个buffer中,这个buffer会存储到状态里,直到窗口触发输出的时候,才把所有数据拿出来统一进行计算
EMIT触发
一个窗口一般只在结束的时候输出结果,EMIT可以将该窗口的中间结果输出
小结
- 1.三种窗口的定义(滚动,滑动,会话)
- 2.迟到数据处理:Allowlateness,SideOutput
- 3.增量计算和全量计算模型
- 4,EMIT触发提前输出窗口结果
Window 高级优化
Mini-batch优化
分析:Flink的数据流在动态表中的更新时,会产生retract流,mary在第一次读取是,数量加一,第二次读取到时,向之前的mary通知retract流,更新mary为2。1)这样一个过程会造成中间量过大,2)如果放在内存会造成内存使用过大,如果放在外部,会造成过多的序列化和反序列化,导致过高的cpu使用率。
简单理解:让算子攒一小批数据,对这一小批读取一次状态
倾斜优化 Local-global
分析:有些key的数据较多,如果shuffle会导致该结点压力过大
方法:通过在之间将数据进行运算处理,减少数据量再shuffle
Distinct状态复用
在流式中,对于count distinct这种情况,我们是需要保存所有数据是否出现过这样子的一个映射。
滑动窗口pane复用
不同的窗口中共同的数据太多的话,就会有重复的状态存储。
优化方法:我们将滑动窗口划分为更小的粒度,在窗口输出是再将这些结点merge
小结
- 1.Mini-batch优化解决频繁访问状态的问题
- 2.Local-global优化解决倾斜问题
- 3.Distinct状态复用降低状态量
- 4.滑动窗口pane复用降低滑动窗口的状态存储量
案例分析
案例一:计算实时抖音的日活曲线
技术组合:Flink+滚动窗口(1day)+emit
但有点问题,单进程速度太慢
案例二:计算大数据任务的资源使用
问题描述:大数据任务(特指离线任务)运行时通常会有多个container启动并运行,每个container在运行结束的时候,YARN会负责将它的资源使用(CPU、内存)情况上报。一般大数据任务运行时间从几分钟到几小时不等。
需求:根据YARN上报的各个container的信息,在任务结束的时候,尽快的计算出一个任务运行所消耗的总的资源。假设前后两个container结束时间差不超过10min。
会话窗口的场景应用:
总结
- 介绍了流式计算与批式计算
-
- 介绍了watermark的含义,生成,传递,以及如何处理部分断流问题
-
- 介绍了3中基本的窗口的定义,以及迟到数据处理,增量计算与全量计算,emit输出,和几个winodw高级优化方法
标题:流式计算中的 Window 计算| 青训营笔记
网址:juejin.cn/