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

97 阅读4分钟

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

1、流计算概述


(1)处理时间窗口

  • 实时计算:处理时间窗口
  • 数据实时流动、计算,窗口结束直接发送结果,不需要同期调度任务image.png

(2)处理时间 VS 事件时间

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

(3)事件时间窗口

  • 实时计算:事件时间窗口
  • 数据实时进入到真实事件发生的窗口中进行计算,可以有效的处理数据延迟和乱序 image.png
    • 窗口结束时间:没办法预知。故引出:Watermark

(4)Watermark

  • 在数据中插入一些watermark,来表示当前的真实时间 image.png
  • 在数据乱序的时候,watermrk 比较重要,它可以用来在乱序容忍和实时性之间做一个平衡 image.png

2、关于Watermark


(1)Watermark了解

  • Watermark 表示当前真实的事件时间
  • 如何产生:
SQL:
CREATE TABLE order(
   user BIGINT,
   product STRING,
   order_time TIMESTAMP(3),
   WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (. . .);

DataStream:
WatermarkStrategy
       ·<Tuple1<Long, String>>forBoundedOutOfOrderness(Durations(20))
       ·withTimestampAssigner((event, timestamp) -> event.f0);
  • 如何传递: image.png
  • 如何通过 Flink UI 观察 Watermark: image.png image.png

(2)典型问题

  • Per-partition VS per-subtask watermark 生成

    • Per-subtask watermark 生成
      • 早期版本都是这种机制。如果一个 source subtask 消费多个 partition,那么多个partition 直接按的数据读取可能会加剧乱序程度。
    • Per-partition watermark 生成
      • 新版本引入了基于每个 partition 单独的 watermark 生成机制,这种机制可以有效避免上面的问题。
  • 部分 partition/subtask 断流

    • 根据上面提到的 watermark 传递机制,下游 subtask 会将上游所有 subtask 的 watermark 值的最小值作为自身的 watermark 值。如果上游有一个 subtask 的 watermark 的不更新了,则下游的 watermark 都不更新。
    • 解决方案:Idle source
      • 当某个 subtask 断流超过配置的 idle 超过时间时,将当前 subtask 设置为 idle,并下发一个 idle 的状态给下游。下游在计算自身 watermark 的时候,可以忽略掉当前是 idle 的那些 suatask。
  • 迟到数据处理

    • 因为 watermark 表示当前事件发生的真实时间,那晚于 watermark 的数据到来时,系统会认为这种数据是迟到的数据。
    • 算子自身来决定如何处理迟到数据
      • Window 聚合:默认丢弃
      • 双流 JOIN:如果是 OUTER JOIN,则认为他不可能 JOIN 到任何数据
      • CEP:默认丢弃

3、Window


(1)基本功能

  • Window 分类

    • Tumble Window(滚动窗口) image.png
      • 窗口划分:每个 key 单独划分;每条数据只属于一个窗口
      • 窗口触发:Window 结束时间到达的时候一次性触发
    • Sliding Window(滑动窗口) image.png
      • 窗口划分:每个 key 单独划分;每条数据可能会属于多个窗口
      • 窗口触发:Window 结束时间到达的时候一次性触发
    • Session Window(会话窗口) image.png
      • 窗口划分:每个 key 单独划分;每条数据会单独地划分一个窗口如果 Window 之间有交集,则会对窗口进行 merge
      • 窗口触发:Window 结束时间到达的时候一次性触发
  • 数据迟到

    • 定义: image.png
    • 产生情况:只有事件时间下才会有迟到的数据
    • 默认处理:丢弃
    • 迟到数据处理: image.png
      • Allow lateness:这种方式需要设置一个允许迟到的时间。设置之后,窗口正常计算结束后,不会马上清理状态,而是会多保留 allowLateness 一样长的时间;如果还有数据在这段时间到来,则继续之前的状态进行计算。(适用于:DataStream、SQL)
      • SideOutput(侧输出流):这种方式需要对迟到数据打一个 tag,然后在DataStream 上根据这个 tag 获取到迟到数据流,然后业务层面自行选择进行处理。(适用于:DataStream)
  • 增量计算 image.png

    • 每条数据到来,直接进行计算,window 只存储计算结果。比如计算 sum,状态中只需要存储 sun 的结果,不需要保存每条数据。
    • 典型的 reduce、aggregate 等函数都是增量计算。
  • 全量计算

    • 每条数据到来,会存储到 window 的 state 中,等到window 触发计算的时候,将所有数据拿出来一起计算。
    • 典型的 process 等函数都是全量计算 image.png
  • EMIT 触发

    • EMIT:在 Window 没有结束的时候,提前把 window 计算的部分输出出来。
    • 实现:
      • DataStream 里面通过自定义 Trigger 来实现,Trigger 的结果可以是:FIRE(触发计算,但是不清理)、CONTINUE、PURGE、FIRE_AND_PURGE...
      • SQL 里面通过配置:table.exec.early-fire.enabled=true,或者 table.exec.early-fire.delay=(time)

(2)高级优化

  • mini-batch 优化 image.png image.png

  • 倾斜优化 —— local-global image.png

  • Ddistinct 计算状态复用 image.png image.png

  • Pane 优化 image.png image.png


4、案例分析


  • 问题:所以数据都需要在一个 subtask 上完成窗口计算,无法并行 image.png
    • table.exec.early-fire.enabled = true
    • table.exec.early-fire.delay = 5min 通过两阶段聚合来把数据打散,完成第一轮聚合,第二轮聚合只需要对各个分桶的结果求和即可。 image.png
    • table.exec.window allow-retract - input = true