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

176 阅读9分钟

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

1.概述

简述流式计算的基本概念,与批式计算相比的难点和挑战

①流式计算vs批式计算

数据价值:实时性更高,数据价值更高

image.png

②批处理

批处理模型典型的数仓架构是T+1结构,即数据计算是天级别的,当天只能看到前一天的计算结果,通常使用的计算引擎为Hive或者Spark等。计算的时候,数据是完全ready的,输入和输出都是确定性的

image.png

小时级批计算

image.png

  • 批处理除了计算外,还存在资源申请、释放的周期调度的过程,耗时,未必能在一小时内完成

③处理时间窗口

实时计算:处理时间窗口

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

image.png

④处理时间vs事件时间

处理时间:数据在流式计算系统中真正处理时所在机器的当前时间

事件时间:数据产生的时间,比如客户端、传感器、后端代码等上报数据时的时间

image.png

⑤事件时间窗口

实时计算:事件时间窗口

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

问题:什么时候窗口才算结束

⑥Watermark

在数据中插入一些watermark,来表示当前的真实时间

image.png

在数据存在乱序的时候,watermark就比较重要了,它可以用来在乱序容忍和实时性之间做一个平衡

image.png

⑦小结

  1. 批式计算一般是T+1数仓架构
  2. 数据实时性越高,数据的价值越高
  3. 实时计算分为处理时间和事件时间
  4. 事件时间需要Watermark配合来处理乱序

2.Watermark

Watermark的含义、生成方法、传递机制,以及一些典型场景的问题和优化

①Watermark含义、生成方法

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

生成watermark

image.png

②如何传递Watermark

拓扑中的每个节点取上游传递来的所有watermark中数值最小的,并向下游的所有节点传递其watermark数值

image.png

③如何通过Flink UI观察Watermark

image.png

④典型问题

1.Per-partition VS per-subtask watermark生成

  • Per-partition watermark生成

    早期版本都是这种机制。典型的问题是如果一个source subtask消费多个partition,那么多个partition之间的数据读取可能会家具乱序程度

  • Per-subtask 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.迟到数据处理

  • 因为watermark表示当前事件发生的真实时间,那晚于watermark的数据到来时,系统会认为这种数据是迟到的数据

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

    Window聚合,默认会丢弃迟到数据

    双流join,如果是outer join,则可以认为它不能join到任何数据

    CEP(复杂事件处理),默认丢弃

⑤小结

  1. 含义:表示系统认为的当前真实时间
  2. 生成:可以通过Watermark Generator来生成
  3. 传递:取上游所有subtask最小值
  4. 部分数据断流:Idle Source
  5. 吃到数据处理:Window算子是丢弃;join算子则认为跟之前的数据无法join到

3.Window

Window基本功能和高级优化

①基本功能

1.Window分类

  • 典型的Window

    Tumble Window(滚动窗口)

    • 数据时间唯一确定窗口,窗口大小不变,只是过了时间间隔后窗口向后滚动

    • 窗口划分

      每个key单独划分

      每条数据只会属于一个窗口

    • 窗口触发

      Window结束时间到达的时候一次性触发

    Sliding Window(滑动窗口)

    • 数据时间唯一确定窗口

    • 窗口划分

      每个key单独划分

      每条数据可能会属于多个窗口

    • 窗口触发

      Window结束时间到达的时候一次性触发

    Session Window(会话窗口)

    • 窗口划分是一个动态的过程

    • 窗口划分

      每个key单独划分

      每条数据会单独划分为一个窗口,如果window之间有交集,则会对窗口进行merge

    • 窗口触发

      Window结束时间到达的时候一次性触发

  • 其他Window

    全局Window

    Count Window

    累计窗口

2.窗口的使用

image.png

3.迟到数据处理

  • 如何定义迟到

    一个数据到来后,会用WindowAssigner给它划分一个window,一般时间窗口是一个时间区间,如果划分出来的window end比当前的watermark值还小,说明这个窗口已经触发计算了,这条数据会被认为是迟到数据

  • 什么情况下会产生迟到数据

    只有事件时间下才会有迟到的数据

  • 迟到数据默认处理

    丢弃

  • 其他处理方式

    • Allow lateness

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

      适用于:DataStream、SQL

    • SideOutput(侧输出流)

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

      适用于:DataStream

4.增量VS全量计算

  • 增量计算

    • 每条数据到来,直接进行计算,window只存储计算结果。比如计算sum,状态中只需要存储sum的结果,不需要保存每条数据(每次数据处理的覆盖使用retract机制)
    • 典型的reduce、aggregate等函数都是增量计算
    • SQL中的聚合只有增量计算
  • 全量计算

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

5.EMIT触发

  • 什么是EMIT

    通常来讲,window都是在结束的时候才能输出结果,比如1h的tumble window,只有在一个小时结束的时候才能统一输出结果

    如果窗口比较大,比如1h或者1天,甚至于更大的话,那计算结果输出的延迟就比较高,失去了实时计算的意义

    EMIT输出指的是:在window没有结束的时候,提前把window计算的部分结果输出出来(同样用到了retract机制)

  • 如何实现

    在Datastream里面可以通过自定义Trigger来实现,Trigger的结果可以是:CONTINUE、FIRE(触发计算,但是不清理)、PURGE、FIRE_AND_PURGE

  • SQL也可以使用,通过配置

    table.exec.emit-fire.enabled=true

    table.exec.emit-fire.delay={time}

②高级优化

  1. Mini-batch优化

    (增量计算)正常情况下,每来一条数据都会产生一个retract流

    状态分为:放在内存的状态和放在外部的状态

    读状态、更新状态、写回状态,访问状态非常频繁,每一次访问都需要做序列化和反序列化,这样对CPU开销很大

    • 优化:先攒一批数据,然后集中处理,减少对状态的访问,但是会有延迟,可以借用watermark的机制,上游向下游发送mini-batch标识
  2. 倾斜优化——local-global

    不同key的数据量可能会相差很大,因此在将上游数据发送给下游前,可以先做一次聚合(local),再发给下游(global)

  3. Distinct计算状态复用

    数据库系统或者批计算对distinct的处理都是先做group by再count,而在流处理中若这样处理会让窗口难以处理,因此可以将distinct信息保存到状态中,如果每一个count distinct都需要一个状态,那么状态量会非常大

    若如图所示,那么可以让一个指标的多个FILTER条件复用一个状态,bit value表示不同的filter条件下,对应的key是否来过

  4. Pane优化

    滑动窗口中,单条数据可能会属于多个窗口,若滑动时间在窗口大小所占比例过小(一天的窗口,1h的滑动),那么每条数据会被重复处理多次

    将一个窗口进行划分,使得每条数据近似属于一个窗口,但是实际上仍是(如例)三个连续的pane属于一个窗口,但是最后在窗口输出结果时需要进行一个merge(只是在计算和存储方面进行优化)

  5. 小结

    • Mini-batch优化解决频繁访问状态的问题
    • local-global优化解决倾斜问题
    • Distinct状态复用降低状态量
    • Pane优化降低滑动窗口的状态存储量

4.案例分析

抖音DAU实时曲线计算,大数据任务资源使用实时统计分析

①需求一:使用Flink SQL计算抖音的日活曲线

做一个滚动窗口,然后使用EMIT机制,让窗口提前输出

问题:所有的数据都需要在一个subtask中完成窗口计算(最后的聚合),无法并行

改进:通过两阶段聚合来把数据打散,完成第一轮聚合,第二轮聚合只需要对各个分桶的结果求和即可(相当于倾斜优化)

②需求二:使用Flink SQL计算大数据任务的资源使用

image.png

典型的可以通过会话窗口来将数据划分到一个Window中,然后再将数据求和即可

5.小结

  1. 流式计算基本概念,以及和批式计算的区别
  2. watermark的含义、如何生成、如何传递,以及如何处理部分partition断流的问题
  3. 介绍了三种基本的window的定义,以及迟到数据处理、增量计算VS全量计算,EMIT输出;同时介绍了local-global优化,mini-batch优化,distinct状态优化,滑动窗口的pane优化等
  4. 两个案例介绍滚动窗口、会话窗口,以及两阶段聚合解决倾斜问题