流计算中的 Window 计算 | 青训营笔记

106 阅读6分钟

流计算中的 Window 计算 | 青训营笔记

这是我参与「第四届青训营 」笔记创作活动的的第4天,本篇笔记主要是关于第四次大数据课程《流计算中的 Window 计算》的课堂笔记


流式计算VS批式计算

数据价值:实时性越高,数据价值越高 image.png

处理时间窗口

数据实时流动,实时计算,窗口结束直接发送结果,不需要周期调度任务

处理时间: 数据在流式计算系统中真正处理时所在机器的当前时间。
事件时间: 数据产生的时间,比如客户端、传感器、后端代码等上报数据时的时间。

第一阶段小结:

  1. 批式计算一般是T+1的数仓架构

T + 1:即数据计算时天级别的,当天只能看到前一天的计算结果。

  1. 实时计算分为处理时间和事件时间
  2. 事件时间需要Watermark配合来处理乱序

Watermark:

表示系统认为的当前真实的事件时间

如何传递Watermark?

类似于上节课中介绍的Checkpoint的制作过程,上下游task之间有数据传输关系的,上游就会将Watermark传递给下游,每一个算子根据上游传递的Watermark的最小值来决定自身的Watermark,并向下游传递自己的Watermark。 image.png 第二阶段小结:

  1. 含义: 表示系统认为的当前真实时间
  2. 生成: 可以通过Watermark Generator来生成
  3. 传递: 取上游所有subtask的最小值
  4. 部分数据断流: Idle Source

Idle Source:数据断流是很常见的问题,有时候是业务数据本身就有这种特点,比如白天有数据,晚上没有数据。在这种情况下,watermark默认是不会更新的,因为它要取上游subtask发来的watermark中的最小值。此时我们可以用一种IDLE状态来标记这种subtask,被标记为这种状态的subtask,我们在计算watermark的时候,可以把它先排除在外。这样就可以保证有部分partition断流的时候,watermark仍然可以继续更新。

  1. 迟到数据处理: Window算子是丢弃; Join算子认为跟之前的数据无法join到正常数据上

Window

Window的几种分类:

  • TUMBLE Window (滚动窗口) 根据数据的时间(可以是处理时间,也可以是事件时间)划分到它所属的窗口中windowStart = timestamp - timestamp % windowSize,这条数据所属的window就是[windowStart, windowStart + windowSize) image.png
  • HOP Window (滑动窗口) TUMBLE窗口是每条数据只会落在一个窗口中。在HOP窗口中,每条数据是可能会属于多个窗口的 image.png
  • SESSION Window (会话窗口) 会话窗口,是一个动态merge的过程。一般会设置一个会话的最大的gap,比如10min。

那某个key下面来第一条数据的时候,它的window就是 [event_time, event_time + gap),当这个key后面来了另一条数据的时候,它会立即产生一个窗口,如果这个窗口跟之前的窗口有overlap的话,则会将两个窗口进行一个merge,变成一个更大的窗口,此时需要将之前定义的timer取消,再注册一个新的timer。 image.png


迟到数据处理

  1. 怎么定义迟到? 一条数据到来后 ,会用WindowAssigner给它划分一个window,一般时间窗是一个时间区间,比如[10:00,11:00],如果划分出来的window end比当前的watermark值还小,说明这个窗口已经触发了计算了,这条数据会被认为是迟到数据。
  1. 什么情况下会产生迟到数据? 只有事件时间下才会有迟到的数据。
  1. 迟到数据默认处理? 丢弃

  2. 另外两种处理方法

    1. Allow lateness

    这种方式需要设置一个允许迟到的时间。 设置之后,窗口正常计算结束后,不会马上清理状态而是会多保留 allowl ateness 这么长时间,在这段时间内如果还有数据到来则继续之前的状态进行计算。

    1. SideOutput (侧输出流)

    这种方式需要对迟到数据打一个tag,然后在DataStream上根据这个tag获取到迟到数据流,然后业务层面自行选择进行处理。

窗口计算模型:

  • 增量计算:每条数据到来后,直接参与计算(但是还不需要输出结果)。不需要存储所有buffer的数据,状态量会小很多。
  • 全量计算:每条数据到来后,先放到一个buffer中,这个buffer会存储到状态里,直到窗口触发输出的时候,才把所有数据拿出来统一进行计算

EMIT:

  • EMIT输出:在window没有结束的时候,提前把window计算的部分结果输出出来。

Window高级优化

Mini-batch优化

核心思想:攒一小批数据再进行计算,这批数据每个key的state访问只有一次,这样在单个key的数据比较集中的情况下,对于状态访问可以有效的降低频率,最终提升性能。

倾斜优化——Local-global

将原本的聚合划分成两阶段,第一阶段先做一个local的聚合,这个阶段不需要数据shuffle,是直接跟在上游算子之后进行处理的;第二个阶段是要对第一个阶段的结果做一个merge image.png

Distinct状态复用

对同一个字段用不同的filter来进行count distinct的计算,把相同字段的distinct计算用一个map的key来存储,在map的value中,用一个bit vector来实现就可以把各个状态复用到一起了。 image.png

滑动窗口pane复用

滑动窗口一条数据可能会属于多个window。所以这种情况下同一个key下的window数量可能会比较多。

eg:3个小时的窗口,1小时的滑动的话,每条数据到来会直接对着3个窗口进行计算和更新。这样对于状态访问频率是比较高的,而且计算量也会增加很多。

优化方法:

  1. 将窗口的状态划分成更小粒度的pane,比如上面3小时窗口、1小时滑动的情况,可以把pane设置为1h,这样每来一条数据,我们就只更新这条数据对应的pane的结果就可以了。
  2. 当窗口需要输出结果的时候,只需要将这个窗口对应的pane的结果merge起来就可以了。 image.png
  • 第三阶段小结:
    1. Mini-batch 优化解决频繁访问状态的问题
    2. local-global 优化解决倾斜问题
    3. Distinct 状态复用降低状态量
    4. Pane 优化降低滑动窗的状态存储量