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

116 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的的第1天

一.概述

1.流式计算和批式计算的比较

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

2.批处理

批处理模型典型的数仓架构为T+1架构,即数据计算时天级别的,当天只能看到前一天的计算结果。 通常使用的计算引擎为Hive或者Spark等,计算的时候,数据是完全ready的,输入和输出都是确定性的。 若想批处理做到更实时->从一天变到小时 一天做24次。但缺点是:除了计算外还要申请释放周期调度过程,耗用资源过多;计算的时间长度不同,一小时无法完成结果计算。因此不现实。

3.处理时间窗口

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

4.处理时间和事件时间

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

5.事件时间窗口

实时计算:事件时间窗口 数据实时进入到真实事件发生的窗口中进行计算,可以有效的处理数据延迟和乱序。 什么时候窗口才算结束?用watermark判断

6.Watermark

image.png

在11后面不存在比11小的数据,若存在,则视为延迟数据,不影响11前面的数据的处理

二.Watermark

1.含义:表示系统认为的当前真实的事件时间

2.产生

image.png

3.传递

image.png

每个算子根据上游传递的最小值来决定自己的数值,同时要给下游传递数值。

4.如何通过Flink UI观察Watermark

通过Flink的 UI判断watermark是否正常 拿到算子的watermark 是否在到达时间范围

5.典型问题

(1)Per-partition VS per-subtask watermark 生成 Per-subtask watermark生成 早期版本都是这种机制。典型的问题是如果一个source subtask 消费多个 partition,那么多个partition 之间的数据读取可能会加剧乱序程度。 Per-partition 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)迟到数据处理: 当数据到达时间超过原先设定时间,则称该数据为迟到数据
  • 算子自身来决定如何处理迟到数据:
    • Window聚合,默认会丢弃迟到数据;
    • 双流oin,如果是outer join,则可以认为它不能join到任何数据;
    • CEP,默认丢弃。

三.Window

1.分类:滚动窗口/滑动窗口/累计窗口/其它

2.使用:

层次越高,抽象层度越高,用户使用成本越低,表达能力更有限 image.png\

  • 滚动窗口
    窗口划分:每个key单独划分;每个数据属于一个key
    窗口触发:Window结束事件到达的时候一次性触发
  • 滑动窗口
    窗口划分:每个key单独划分;每条数据可能会属于多个窗口
    窗口触发:Window结束时间达到的时候一次性触发
  • 会话窗口
    窗口划分:每个窗口单独划分,根据每个数据再去划分窗口,如果window间有交集,就将两个window合并
    窗口触发:Window结束时间到达的时候一次性触发\

3.怎么定义迟到数据?

一条数据到来后,会用WindowAssigner 给它划分一个 window,一般时间窗口是一个时间区间,比如[10:00,11:00),如果划分出来的 window end 比当前的 watermark 值还小,说明这个窗口已经触发了计算了,这条数据会被认为是迟到数据。(若窗口范围但晚于watermark,也不算迟到)

  • 只有事件时间才有迟到数据 迟到数据默认丢弃

4.处理迟到数据

  • Allow lateness
    设置一个允许迟到时间,窗口正常时间结束后,不会马上清理状态,而是多保留allowLateness段时间,在该段时间到来的数据,仍正常处理(适用于DataStream\sql)
  • SideOutput
    对迟到数据打一个tag,然后在DataStream是根据这个tag获取迟到数据流,然后业务层面自行选择处理。(适用于DataStream)

5.增量计算和全量计算

增量计算:每条数据到来,直接进行计算,window只存储计算结果。 比如计算sum,状态中只需要存储sum的结果,不需要保存每条数据。(更优,可以让计算过程更加平缓,可以让存储数据量更少,只需保留中间结果)

  • 典型的reduce、aggregate等函数都是增量计算
  • SQL中的聚合只有增量计算 全量计算:每条数据到来,会存储到window的state中。等到window触发计算的时候,将所有数据拿出来一起计算。(缓存数据较多)
  • 典型的process函数就是全量计算

6.EMIT触发:在窗口没有结束的时候,提前把窗口计算的部分结果输出出来。

实现:在DataStream里面可以通过自定义Trigger实现,

  • Trigger的结果可以是:
    CONTINUE;FIRE;PURGE;FIRE_AND_PUGRE),
  • sql通过配置:
    (1)table.exec.emit.early-fire.enabled=true
    (2)table.exec.emit.early-fire.delay=(time)

7.高级优化

(1)Mini-batch优化:读一小部分数据,处理输出,再继续进行,用于解决中间结果较多,访问频繁的问题。

image.png

基础版本能work,但拓扑复杂,数据延迟严重,要优化。上游发送minibatch机制,下游接受后处理输出。 (2)倾斜优化(local-giobal:在shuffle之前,先预集合,将中间结果再shuffle到下游 image.png

(3)Distinct计算状态复用

image.png (4)Pane优化 将数据划分更小力度pane,一条数据属于一个pane直接计算,在窗口输出结果时merge。

四.案例分析

1.用Flink SQL计算抖音的日活曲线 用滚动窗口和emit机制在窗口计算DISTINCT uid得到一天内用户的uid结果,再用emit输出。 问题:所有数据都在一个subtask完成,无法并行。 改善:通过两阶段聚合将数据打散,完成第一轮聚合,第二轮聚合只需对各个分桶进行结果求和。
2.用Flink SQL计算大数据任务的资源使用

image.png