Flink如何巧用WaterMark机制解决乱序问题

问:数据工程师最指望数据怎么来?ide

答:按顺序来。设计

 

MapReduce当初能用起来,就是由于Map阶段对全部数据都进行排序了,后面的Reduce阶段就能够直接用排序好的数据了。blog

批处理的时候由于数据已经落地了,咱能够慢慢排序。可是流式数据都是一条一条过来的,这个时候数据到达的时间和出发时的顺序不一致会致使很是多的问题,这该咋整呢?排序

Sparkstreaming对乱序支持不好,由于它实际上是“微批”,不是真正的流。加州伯克利大学AMP实验室设计Spark的时候,想的就是弄一个更快的计算引擎,压根就没打算作成来一条处理一条的流式数据处理。因此对于一些乱序数据根本就不太关心,因此致使Sparkstreaming不能或者不太能支持乱序数据的处理。事件

 

可是Flink不行啊,数据一条一条的过来,而后进行窗口处理,乱序会致使各类统计问题,这就得必须解决了。get


 

什么是乱序it

一条数据在Flink里,有三个时间:io

  • Event Time:事件产生的时间;class

  • Ingestion Time:事件进入Flink的时间;stream

  • Window Processing Time:事件被处理的时间。

 

 当数据一条一条规规矩矩的按流程发送,MQ传输,Flink接受而后处理,这个时候,就是有序的数据。

 

 当出现各类异常,有些数据延迟了,排在后面的数据跑前面去了,这就出现了乱序。

 

 请思考一下,咱们应该以哪一个时间戳断定乱序呢?


 

Flink的WaterMark机制

乱序会致使各类统计上的问题。好比一个Time Window本应该计算一、二、3,结果3迟到了,那这个窗口统计就丢数据了。这可太坑了。

 

 为了解决这个问题,Flink设置了一个三个机制来解决这个问题:

  • WaterMark--水位线,;

  • allowLateNess--数据迟到时间;

  • sideOutPut--超长迟到数据收集;

 

水位线的设置很简单(系统时间为准):

override def getCurrentWatermark(): Watermark = {       new Watermark(System.currentTimeMillis - 5000)

设置Watermark为-5秒。可是怎么理解这个-5秒的水位线呢?

 

常常户外徒步的同窗应该知道一个徒步小队一般会有一正两副领队,队首队尾各一个副队,正队长在队伍中穿插协调。

队尾的领队叫后队领队,后队领队要保证全部队员都在前面,也就是说后队领队是整个队伍的队尾,当收队的时候,看见后队领队,那就说明整个队伍都已经彻底到达了。

 

这个Watermark就至关于给整个数据流设置一个后队领队。可是窗口是不知道具体要来几个数的,因此只能设置一个时间上的限制,以此来推测当前窗口最后一条数据是否已经到达。假设窗口大小为10秒,Watermark为-5秒,那么他会作如下事情:

  • 每来一条数据,取当前窗口内全部数据的最大时间戳;

  • 用最大时间戳扣减Watermark后看看是否是符合窗口关闭条件;

  • 若是不符合,则继续进数据;

  • 若是符合,则关闭窗口开始计算。

 

你看,多像户外徒步?

  • 每来一我的,就问问出发时是几号,而后确认全部已到队员最大的号码;

  • 用最大的号码对比一下后队领队的号码;

  • 若是比后队领队的号码小,就不收队;

  • 若是号码大于等于后队领队号码,就收队。


 

迟到的数据

固然啊,即使是用了Watermark机制,依然还会存在迟到的数据。就像户外徒步同样,有人走错路而后又遇上来。后队领队分明没超过任何一个队员,可是仍是有队员落在后面了。

因此Flink还增设了三种应对方式:

  • allowLateNess--对于迟到一小会的数据,设置一个容许迟到时间;

  • sideOutPut--对于超过容许迟到时间的数据,所有收集起来,后续再处理;

  • 若是都不处理,Flink就默认自动丢弃。

 

也就是说,在watermark机制下,窗口虽然到了关闭时间,可是若是你设置了allowLateNess=10秒,那这个窗口还会再等10秒,看看是否还有他那个小队的数据,10秒后窗口关闭,开始计算。

若是等了10秒还没等到,11秒的时候,本来属于该窗口的数据才姗姗来迟,那么sideOutPut会把数据收集起来,放到侧输出流,等待后续处理。这个数据确定就不会在当前窗口计算进去了。

相关文章
相关标签/搜索