说说实时流式计算-LMLPHP

好了先不思考人生了,言归正传。
我们先把 storm/spark/flink 这些花里胡哨的技术都抛开,这些我之后的文章会详细讲,现在就说说实时流式计算本身。流式计算有什么用呢?实际场景会告诉你:

1. 场景

流处理适用场景还是很丰富的,它最大的特点就是及时,试想一些,没有下面的这些流式计算系统,公司会损失多少MONEY:

  • 需要实时异常检测的欺诈/风控等系统
  • 需要实时查看交易额的交易系统
  • 需要实时计算点击/计算分成的广告系统
  • 需要实时更新用户标签的实时用户画像系统
  • 需要实时根据用户喜好推荐商品的实时推荐系统

2. 时间

时间对于批计算来说好像没有什么特别的,就是一个字段而已,但是流式计算里,除了字段里的那个时间(专业点,我们称这个时间为事件时间event-time)还有一个数据到达的时间及处理系统的当前时间(处理时间process-time)。

那问题来了,为什么要管这个处理时间?因为数据会有延迟,你处理的时间批次里,可能会有很久之前的数据延迟到了现在,也有可能现在的数据没有及时到达导致缺数。

不知道看到这里大家会不会想到大数据比较常用的Lambda架构,先提供时效性高准确性较低的结果,然后对之前的数据做矫正,保证最终正确性(当然前提条件是批处理作业启动时,需要的数据应该已经全部到达了)。对于这个问题的解决办法其实是和Lambda架构类似的,后面我会细说。

看上面的文字描述可能会有点抽象,我们先来看看下面这幅图,横轴为事件时间,纵轴为处理时间,圈起来的数字代表真实的数据,它们分别都有事件时间和处理时间,在两者相同的理想情况下,就如同下面浅色的虚线是一条直线,这样是最好处理的,但是实际情况却是很曲折的,如深色的虚线,我们把虚线称为水位线,水位线是根据一定算法根据最近处理的事件的事件时间估算出来的,可以作为事件的触发的一个参考项。

说说实时流式计算-LMLPHP

3. 窗口

上面提到了事件时间和处理时间,写过SparkStreaming的同学应该知道它有一个处理时间的窗口,就是说可以对某个时间窗口内的数据进行聚合或者其他操作,但是这个时间窗口的时间是基于处理时间的,同样会有上面提到的问题,数据延迟了怎么办?

那么理所当然会有人提出基于事件时间的窗口,这个处理方式就是《Google:DataFlow》中提出来的,spark和flink后来都有了相应的工程实现。

4. 触发

所谓触发(Triggers)即时间窗口结束后对数据的处理方式。我们直接来看《DataFlow》中的几种触发机制。

  • 抛弃(discarding):
    触发后,不会保留上次计算结果的数据,因为之后窗口计算的结果和之前的结果不存在相关性。当下游的数据消费者(不管是数据处理管道的内部还是外部)希望触发计算结果之间相互独立(比如对插入的数据进行求和的场景),那么这种情况就比较适用。
  • 累积(accumulating):
    触发后,窗口内容被完整保留住持久化的状态中, 后面延迟的结果会对之前的结果进行矫正。这也是Lambda架构使用的方式,流处理管道产出低延迟的结果,之后被批处理管道的结果覆盖掉
  • 累计和撤回(retraction):
    触发后,在进行累积语义的基础上,计算结果的一份复制也被保留到持久化状态中。当窗口将来再次触发时,上一次的结果值先下发做撤回处理,然后新的结果作为正常数据下发。

撤回的操作适用于数据处理管道有多个串行的 GroupByKeyAndWindow 场景,撤回是必要的,因为同一个窗口的不同触发计算结果可能在下游会被分组到不同键中去,这句话是关键,不知道大家有没有理解,简单地说就是,触发时间的变化可能会导致这条延迟数据被分配到的组的变化,从而导致后续的聚合计算不准确,所以需要把之前的数据撤回,带上这条数据一起再做一次GroupByKeyAndWindow。

5. 结语

这一篇提到的很多概念和名字很多同学可能一时会比较难消化,确实比较抽象,所以没关系,后面讲到flink的时候会举具体的例子给大家看,这一篇只是和大家介绍下流式计算以及在流式计算中你需要重点关注的几个点:时间/窗口/触发,记住这三个词也是一个收获。

另外,大家有精力有时间有兴趣的话,推荐大家好好看看Google在2015年发布的一篇关于实时流式计算的论文,《The Dataflow Model》,这篇论文催生了spark的structed streaming以及给了当初默默无闻的flink成就现在辉煌的灵感。用词好像有点浮夸了,但是它确实是在流式计算领域很有指导性意义的一篇论文。

觉得有价值请关注 ▼

07-09 14:27