流计算中的Window机制

101 阅读5分钟

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

流计算中的Window机制

概述

  • 流式和批式

  • 批处理

批处理的典型数仓架构为T+1架构,即数据计算是天级别的,当天只能看到前一天的计算结果。

通常使用计算引擎为Hive或Spark等,计算的时候,数据是完全准备好的,输入和输出都是确定性的。

  • 小时批处理

原本一天收集一次数据进行计算,改为每小时收集一次数据进行计算,以此追求更实时的数据。但由于收集处理的过程是一个周期调度任务,往往很难实现

  • 处理时间的窗口

实时计算:处理时间的窗口

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

  • 处理时间VS事件时间

处理时间:数据在流式计算系统中真正处理的时间

事件时间:数据产生的时间,比如客户端,传感器,后端代码上报数据的时间。不稳定,可能随用户所在地的网络环境的变化而变化

理想状态下处理时间和事件时间是同一时间,但现实中处理时间通常比事件时间有一定的延迟

  • 事件时间的窗口

实时计算:事件时间的窗口

数据实时进入到真实事件发生的窗口中去计算,可以有效的处理数据延迟和乱序

问题:不知道窗口什么时候结束

  • Watermark

在数据中插入Watermark,表示当前真实时间

数据乱序时,watermark很重要,可以用来*乱序容忍实时性之间做一个平衡

Watermark

  • 什么是Watermark?

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

  • 如何产生Watermark
creat table Orders(
    user BIGINT,
    product string,
    order_time timestamp(3),
    watermark for order_time AS order_time_interval '5' second )with(....);
即从原始数据里面order_time字段减去5秒作为watermark数值
  • 如何传递Watermark(取上游所有subtask的最小值)

  • 典型问题1

per-partition VS per-subtask watermark

per-subtask watermark(每个子任务):如果一个source消费多个分区,那么多个分区之间的数据读取可能会加剧乱序程度

per-partition(每个分区):基于每个分区单独的watermark机制,可以有效避免上述问题

  • 典型问题 2

部分partition/subtask断流

根据watermask传递机制,下游subtask会将上游所有subtask的watermask值的最小值作为自身的watermask值,如果上游有一个subtask的watermark不更新了,则下游的watermark就都不更新,即发生断流

解决:Idle source

当某个subtask断流超过idle的超时时间时,将当前的subtask设置为idle,并下发一个idle状态给下游,下游在计算自身的watermark时,自动忽略当前是idle的subtask

  • 典型问题3

迟到数据的处理

watermark是表示当前事件发生的真实时间,晚于watermark的数据就是迟到数据

算子自身决定如何处理迟到的数据

  1. window聚合:默认丢弃
  2. 双流join,如果是outer join,则可以认为它不能join到任何数据
  3. CEP:默认丢弃

Window

基本概念

  • Window分类(最典型的三种)
  1. Tumble Window(滚动窗口)
  2. Slinding Window(滑动窗口)
  3. Session Window(会话窗口)
  • Window的使用

SQL API:

select 
    user,
    tumble_start(order_time,interval'1'day)as wStart,
    sum(amount)from Orders
group by
    tumble (order_time,interval '1'day),--在聚合的地方加一个窗口
    user
  • 滚动窗口

窗口划分:

  1. 每个key单独划分
  2. 每条数据 只属于一个窗口

窗口触发:窗口结束的时候一次性触发

  • 滑动窗口

和滚动窗口的区别:每条数据可能属于多个窗口

触发:结束时触发

  • 会话窗口

和前两者的区别是:一条数据到来时不知道它所属的窗口,每条数据会单独划分一个窗口,如果窗口之间有交集,则会对两个窗口进行合并

触发:结束时触发

迟到数据处理

  • 迟到定义:一条数据到来时,会被划分到一个窗口,如果窗口结束时间比当前的watermark的值还小,说明该窗口已经结束,触发完计算了,则该数据迟到
  • 只有事件时间会产生迟到数据(处理时间来了就直接算)
  • 迟到数据的默认处理:丢弃
  • 处理方法1:Allow lateness

    • 设置一个允许迟到时间,窗口结束后会多保留allow lateness的时间再清理状态,如果这段时间数据来了,就继续之前的状态计算
    • 适用于SQL、datastream
  • SideOutput流

    • 对迟到数据打一个标签,然后datastream上根据标签获取到迟到的数据流,然后处理
    • 适用于datastream

增量计算VS全量计算

  • 增量计算:来一条算一条,window只存储计算结果
  • 全量计算:来一条先存一条,存到window的state中,等到window触发时,拿出来一起算

EMIT触发

  • 为什么需要emit:通常window只有结束后才可以输出结果,如果窗口很大,1h甚至1天,结果输出的延迟就很高,就失去了实时计算的意义

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

  • 在datastream实现

    • 自定义trigger
  • 在SQL实现(配置)


Window-高级优化

  • Mini-batch优化:优化解决频繁访问状态问题

    问题:①中间结果偏多②状态访问频繁
    
    • 原理:先攒一批数据,然后输出

  • 倾斜优化:优化解决倾斜问题

  • distinct:状态复用降到状态量

  • pane优化:降低滑动窗口的状态存储量

    • 问题:在滑动窗口中,一条数据可能属于多个窗口,这样计算量可能会很大

    • 解决:把原来的一个窗口划分为更小的几个窗口

案例分析

使用flink SQL计算抖音的日活曲线

  • 方法:做一个滚动窗口,用emit提前把窗口内容输出
  • 问题:所有数据都需要在一个subtask中完成窗口计算,无法进行

计算大数据任务时的资源使用

\