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

113 阅读5分钟

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

流计算中的 Window 计算

1.概述

流式计算 vs 批式计算: 实时性越高,数据价值越高

特性批式计算流式计算
数据存储HDFS HivsKafka Pulsar
时效性分钟
准确性精确精确和时效
计算引擎Hive Spark FlinkFlink
模型Exactly-onceat-least-once Exactly-once
资源模型定时调度长期持有
主要场景离线实时数仓、实时风控
  • 实时计算:处理时间窗口
    数据实时流动、实时计算,窗口结束直接发送结果,不需要周期调度任务
  • 事件时间:数据产生的时间
  • 处理时间:数据在流式中真正处理的时间
  • 窗口结束:事件时间延迟不定

2.Watermark

  • def:表示系统认为的当前真实的事件时间(简单而言,就是数据产生时间而不是处理时间)
  • SQL语法: 多个watermark取最小值
CREATE TABLE Orders(
    user BIGINT,
    product STRING,
    order_time TIMESTAMP(3),
    WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
)WIth(...);
  • 问题:
    1. per-partition VS per-subtask watermark生成
      per-subtask:早期版本,多个partiton之间数据可能会加剧乱序程度;
      per-partition:更为高效。
    2. 部分partition/subtask断流
      上游有subtask的watermark不更新,下游的watermark(取最小值)都不更新。
      解决方法:
      Idle source:subtask断流超过idle设定时间时,将subtask标记为idle,并下发一个idle状态给下游,下游在配置watermark时可以忽略idle的subtask
    3. 迟到数据处理
      迟到数据:晚于watermark到来的数据
      • window 聚合,默认丢弃
      • 双流join,outerjoin则可以认为不能join到任何数据
      • CEP,默认丢弃

3.Window

  • window 分类:
    • Tumble window 滚动窗口
      • 窗口划分:每个key单独划分,每条数据只会属于一个窗口;
      • 窗口触发:结束时间到达的时候一次性触发。
    • Sliding window 滑动窗口
      • 窗口划分:每个key单独划分,每条数据会属于多个窗口;
      • 窗口触发:结束时间到达的时候一次性触发。
    • Session window 会话窗口
      • 窗口划分:每个key单独划分,每条数据会单独划分为一个窗口,如果window之间有交集,则会对窗口进行merge
        (eg 10:30-10:40 & 10:35-10:45 -> 10:30 - 10:45)
      • 窗口触发:结束时间到达的时候一次性触发。
  • window 使用:SQL API
SELECT
    user,
    TUMBLE_START(order_time,INTERCAL '1' DAY) AS wStart,
    SUM(amount) FROM Orders
GROUP BY
    TUMBLE(order_time, INTERVAL '1' DAY),
    user
  • 迟到数据处理
    • 迟到:数据晚于watermark(一般),数据晚于watermark且属于的窗口已触发(window)只有在事件时间下会有迟到的数据
    • 默认处理:丢弃
    • 特别处理:
      1. Allow lateness:
        • 设置允许迟到的时间,正常计算结束后不会清理状态,保留allow Lateness的时间,数据到来会继续之前的状态进行计算
        • 修正过程实则为Retract的过程,保证最终语义一致性
        • 适用范围:DataStream、SQL
      2. SideOutput(侧输出流):
        • 迟到数据打tag,根据tag获取迟到数据流,根据业务层面自行选择处理
        • 适用范围:DataStream
  • 窗口计算模型
    • 增量计算:
      • 每来条数据直接进行计算,只储存计算结果,不保留数据;
      • 典型的reduce、aggregate等函数;
      • SQL的聚合只有增量计算。
    • 全量计算:
      • 每条数据到来会存储到state。等到window触发计算时候,所有数据拿出来一起计算;
      • 典型的process函数就是全量计算。
  • EMIT 触发
    允许中间结果可以输出多次,即window没有结束的时候,提前把window计算的部分结果输出出来
    • 实现方式:
      1. DataStream
        通过自定义trigger实现,如CONTINUE、FIRE(触发计算,但不清理)、PURGE、FIRE_AND_PURGE
      2. SQL 配置
        table.exec.emit.early-fire.enabled=true
        table.exec.early-fire.delay={time}

高级优化

  • mini-batch 优化解决频繁访问状态的问题
    • 多条数据一次性读、处理、写
    • 上游添加assigner,开启一个新的minibatch,之前buffer数据输出
  • 倾斜优化(local - global) 优化解决倾斜的问题
    • 问题原因:eg 对不同数据分别进行求和,可能某一类数据量大,而另一类少
    • 预聚合,上游先进行一定的操作,再聚合
  • Distinct 计算状态复用 降低状态量
    • 利用value作为long数据类型(64bit)表征数据是否来到,eg01100。
  • Pane优化 优化降低滑动窗口状态储存量\
    • 滑动窗口在每一个窗口中计算,数据属于多个窗口,计算量大。
    • 结局方案:设置pane定时计算,输出时多个pane先merge再输出

4.案例分析

  • 使用Flink SQL 计算抖音的日活曲线\
    • 方法一:使用滚动窗口,提前EMIT输出
    SELECT
        COUNT(DISTINCT uid) as dau
        TUMBLE_START(event_time, INTERVAL '1' DAY) as wstart,
        LOCALTIMESTAMP AS current_ts
    FROM user_activity
    GROUP BY
        TUMBLE(event_time,INTERVAL '1' DAY)
    table.exec.emit.early-fire.enabled=true
    table.exec.emit.early-fire.delay=5min
    
    • problem:所有数据在一个subtask窗口,无法并行
    • 改进:两阶段聚合来把数据打散,完成第一轮聚合,第二轮聚合只需要对各个分桶的结果求和即可
    SELECT
        SUM(partial_cnt) as dau
        TUMBLE_START(event_time, INTERVAL '1' DAY) as wstart,
        LOCALTIMESTAMP AS current_ts
    FROM(
        SELECT
            COUNT(DISTINCT uid) as partial_cnt,
            TUMBLE(event_time,INTERVAL '1' DAY) as event_time
        FROM user_activity
        GROUP BY
            TUMBLE(event_time, INTERVAL, '1' DAY)
            MOD(UID,10000) #根据uid分成10000个桶
         )
    GROUP BY TUMBLE(event_time,INTERVAL '1' DAY)
    table.exec.emit.early-fire.enabled=true
    table.exec.emit.early-fire.delay=5min
    table.exec.window.allow-retract-input=true
    
  • 计算大数据任务的资源使用\
    • 大数据离线任务运行时通常会有多个container启动并运行,每个container运行结束时,YARN负责将他的资源使用(CPU、内存)情况上报。需要根据YARN上报的各个container的信息,在任务结束时,尽快的算出一个任务运行所消耗的总资源。
    • 通过会话窗口数据划分到一个window,再结果求和
SELECT
    application_id
    SUM(cpu_usage) as cpu_total
    SUM(memory_usage) as memory_total
FROM resource_usage
GROUP BY
    application_id,
    SESSION(event_time, INTERVAL '10' MINUTE)

Conclusion

  • 学习了流式计算的基本概念
  • 学习了watermark的生成运行方式,其中有关迟到数据、watermark的生成方式及数据断流进行了进一步的拓展
  • 学习了window的基本分类,以及迟到数据处理、增量or全量计算、EMIT输出的概念和实现方式
  • 学习了四种高级优化:local-global、mini-batch、distinct计算状态复用、pane优化