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

145 阅读8分钟

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

1. 实时计算和批式计算的本质区别

2. 实时计算中的核心功能:watermark机制、window机制

3. 了解3大基本窗口类型的定义、使用以及核心原理

4. 窗口机制中的最核心的优化和原理

 

 

批处理模型典型的数仓架构为T+1架构(即当天只能看到前一天的计算结果)。

通常的计算引擎为Hive和Spark,数据计算以天级别。

小时级批计算面临的问题:除了计算之外,申请、调度也消耗很资源;数仓计算的时间业务、建模是分层的,很多情况下是做不到短时间内的数据的产生和完成处理。

 

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

实时计算分成:处理时间和事件时间。

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

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

处理时间和事件时间.png

事件时间和处理时间之间在实际情况下存在延迟。

 

实时计算:事件时间窗口

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

事件时间窗口.png

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

   

Watermark

Watermark:表示系统认为的当前真实的事件时间。Watermark通过watermark generator来生成。

在数据中插入一些watermark,用于表示当前的时间。

watermark插入.png

Watermark的功能讲例:

1. watermark设为11,若后来来的数据的时间值小于11,则认为这个数据为延迟数据,不将其加入到前面的数据处理中。不影响之前的窗口

2. 用于在乱序容忍和实时性做平衡。(watermark间隔短则乱序容忍小,实时性高)

   

产生Watermark.png

Watermark传递(和checkpoint有相似之处,可互相参照比对)

watermark传递.png

多source的watermark传递,下游接收到多个watermark的话,取其中的最小值作为当前算子的watermark,再往下传递。

典型问题:为什么自己开发的flink窗口的时间窗口的程序,有数据输入但没有数据输出?

需要怀疑watermark是否正常。

 

通过Flink UI观察Watermark

watermark检查.png

watermark检查1.png

根据时间戳看是否正常,上游数据不够多或异常,或者数据有但watermark不太对,window里的数值比较小、还没到触发窗口的输出时间。

 

  典型问题一:

生成watermark的方式的不同

  •  per-subtask watermark生成:

若一个source subtask消费多个partition,那么多个partition之间的数据读取可能会加剧乱序。

  •  Per-partition watermark生成:

给予每个partition单独的watermark生成机制,可避免上面的问题。

 

典型问题二:

  •  部分partition/subtask断流:如果上游有一个subtask的watermark不更新了,则下游的watermark都不更新了。(因为下游要取上游的最小值)

解决方案:Idle source

当某个subtask(算子)断流超过配置的idle超时时间时,将当前subtask置为idle,并下发一个idle状态给下游。下游在计算自身watermark的时候,可以忽略掉当前是idle的subtask。

 

典型问题三:

  •  迟到数据处理:因为watermark表示当前事件发生的真实时间,晚于watermark来的数据系统认为是迟到数据。

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

-window聚合,window算子会默认丢弃迟到数据

-双流join,如果是outer join,join算子则认为此数据不能join到任何数据

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

 

Window

Window分类

1. tumble window(滚动窗口)

2. Sliding window(滑动窗口)

3. Session window(会话窗口)

另外还有:全局window、count window、累计窗口。。。。

 

Window使用

window使用.png

此图内:层次越高,抽象程度越高,用户使用成本越低。

滚动窗口

窗口划分:

1. 每个key单独划分(例图中的user1、user2、user3)

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

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

滚动窗口.png  

滑动窗口

窗口划分:

1.每个key单独划分(例图中的user1、user2、user3)

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

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

滑动窗口.png

 

会话窗口

窗口划分:

1.每个key单独划分(例图中的user1、user2、user3)

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

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

会话窗口.png

以时间划分窗口(比如十分钟的窗口,但是内部数据可能来得快也可能来的慢,窗口内的数据尺寸不固定)

窗口会合并(十分钟的窗口,0-10min一个窗口,在5min(5-15)时来了个数据,则窗口扩展为0-15min)

 

 

迟到数据处理

怎么定义迟到?

一条数据到来后,会用windowassigner划分一个window,一般时间窗口是一个时间区间,比如[10:00,11:00),如果划分出来的window end比当前的watermark还小,说明这个窗口的已经触发了计算,会被认为是迟到数据。(宽松一点的话,窗口不触发计算,则可以认为没迟到,后续可进行处理)

只有事件时间才会有迟到的数据。处理时间不会又迟到时间。

迟到数据默认丢弃。

 

迟到数据处理方式

1. Allow laterness

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

适用于Datastream、SQL

这是一种“修正”的方法,含有retract的机制。

 

2. Sideoutput(侧输出流)

对迟到数据打一个tag,然后在Datastream上根据这个tag获取到迟到数据流,然后业务层面自行选择进行处理。

适用于Datastream

 

 

增量和全量计算(窗口计算模型)

增量计算:

-每条数据到来,直接进行计算,window只存储计算结果,比如计算sum,状态中只需要sum的结果,不需要保存每条数据

-典型的reduce,aggregate等函数都是增量计算

-SQL中的聚合只有增量计算

增量计算.png

全量计算

-每条数据到来,先存储到window的state中,等到window触发计算的时候,将所有数据那出来一起计算。

-process函数就是全量计算

  全量计算.png

 

EMIT触发机制

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

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

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

 

实现:

在datastream里面可以通过自定义trigger来实现,trigger的结果可以是:

-CONTINUE

-FIRE(触发计算,但是不清理窗口状态)

-PURGE

-FIRE_AND_PURGE

 

 

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

-table.exe.emit.early-fire.enabled=ture

-table.exe.emit.early-fire.delay=(time)

 

 

 

高级优化

Mini-batch优化

解决的问题:1.中间结果偏多  2.状态访问比较频繁

思路:算子攒一小批数据,读一次状态,处理这小批数据,然后输出,并写回状态。

minibatch.png

缺点:如果任务的拓扑比较复杂,每个算子都要攒一批数据再处理,其中的延迟会在流程和网络中不断累计,形成巨大延迟。

可以使用checkpoint和watermark机制协调上述问题,统一发出一个指令:开始当前新的一个mini-batch的信号,下游每个算子收到信号后把之前的数据计算输出、写状态,并下传。相当于在一个上游的信号周期内完成整个链路里的算子mini-batch的工作。

 

倾斜优化-local-global

  倾斜优化.png

工作之前需要对数据进行预聚合、预处理,中间结果发到最终的地方去处理。

Distinct计算状态复用

distinct计算复用.png

 

distinct计算状态2复用.png  

Pane优化

pane优化.png

例:一个小时的窗口,一分钟的滑动,滑动/窗口size太小,这样要参与窗口的数据量是巨大的, 计算开销也巨大。

划分成小粒度,不是窗口,但可以组成窗口。

pane优化2.png

 

1. mini-batch优化解决频繁访问状态的问题

2. Local-global优化解决倾斜问题

3. Distinct状态复用降低状态量