本文整理自公司组内集体学习分享的讲稿,简单的介绍了流处理领域的一些基本概念,主要是抄袭Tyler Akidau 的两篇文章 Streaming 101/102。 两篇不长的文章,对流式处理和其解决的问题有很好的诠释。Tyler之后又受邀把这两篇文章扩成 Streaming System 一书,非常值得一看。可惜同事们都没怎么认真读T_T。

考虑到同事大多之前没有接触过流处理(系统),所以这篇文章主要侧重于流处理的概念入门。内容没有涉及具体的框架 API,不讨论流式框架实现的原理, 也不会对概念细节作深度讨论(其实我也不懂)。

什么是流处理

计算机领域中,流(Stream)是一个常见的概念,如TCP 流,视频流,Java Stream API等等。 他们之间的共性是,尽管由一个个独立的元素组成(TCP的Packet,视频的帧等等),对外的接口呈现的是一个连续不断出现的整体(艾玛,太抽象了)。

Tyler对流的定义是:

不断增长,趋于无穷的数据集。A type of ever-growing, essentially infinite data set.

以我司的场景举例,每日的学生上课人数就是一个流,这个流里包含了8月1日 的所有课程的上课的学生的人数,包含了8月2日的,3日的,向未来不断延伸。

流处理(Streaming Processing)就是考虑到流特性的处理模型。由于早期流式处理引擎的不完善,很多人对流处理有不准确的认知,总是将流处理与“低延时”,“估算”关联起来, 为了消歧义,Tyler重新定义了一套名词:

  • 流即无限数据集(Unbounded Data Set)。
  • 对应的,流处理即对无限数据集的处理(Unbounded Data Processing)。

同样的,批处理就成了对有限数据集的处理(Bounded Data Processing)。有限/无限称作数据集的基数(Cardinality)。

人们习惯把流处理和批处理看成两种完全不同的模型,重命名后,我们可以看出,批处理是流处理的一个子集。如果一个模型可以处理无限的数据集,为什么不可以处理有限的数据集呢? 这也解释了另外一个问题:为什么批处理会早成熟若干年。自然而然,处理有限数据集要比处理无限数据集简单。

流式系统的能力

人们对低延迟数据处理的需求是一直存在的,有些信息具有很高的时效性,例如,微博需要尽可能快的展现当前的热点消息, 如果等到一天之后批处理跑完数据,黄花菜都凉了。(Justice delayed is justice denied)

处理有限数据集式,数据是静态完整的。批系统有计算所需要的所有知识。而在处理无限数据集时,情况要复杂的太多。数据流是混乱的,数据的到达可能重复,乱序,丢失。 数据流是不断增长的,怎么能保证系统即便出现崩溃,又能重现相同的结果呢? 在这些问题解决之前,批处理依然要承担主要的数据处理责任。流式系统一段时间内只能作为批式系统的辅助,准确可靠的数据依然要依赖于批式系统。

Twitter提出了Lambda架构,希冀能够同时利用两种模型的好处。 Lambda架构中,同一数据流,会被流式系统和批式处理系统各处理一遍。流式系统提供低延时的估算结果,批式系统提供迟到的正义。不幸的是,这套系统没有取得预期的效果,原因如下:

  1. 一套逻辑维护在两个系统中,是很麻烦的一件事情。
  2. 用户宁愿去看延时的批处理数据,也不愿信任准时的流式系统数据。

Lambda架构的失败,说明了懒惰和缺乏信任是当代社会的两大顽疾。总而言之,修正主义是行不通滴。

搞定了正确性,流式系统就有了和批式系统平起平坐的资格,这是横亘在长征路上的最后一座大山。 Spark Streaming一声炮响,为业界送来了Exactly-once Processing(注意区别Exactly-once delivery),从此流式系统的语义挣脱了At-least once和at-most once,翻身作了主人。 批式系统能准确处理的数据集,流处系统也能准确处理了。

这还不够,流式系统要做无限数据集的处理,还需要一个批式系统不具备的能力:推理时间(Reason about Time)。这是流式系统超越批式系统的地方。

时间域

要推理时间,首先要对时间有所认知。流式系统中,每个数据都存在着若干时间域(Time Domain),如

  • 事件时间(Event Time),即数据真实发生的时间。
  • 处理时间(Processing Time),即系统观察到事件的时间。

理想情况下,事件时间和处理时间相等,事件发生时即可被观察到。现实中自然是不可能的:

  1. 数据的传递本身需要时间。
  2. 网络状况等外界因素的影响,事件时间到处理时间的延迟也不同。

在大多数场景下,结果的正确性需要使用事件时间。现实世界事件时间和处理时间之间不可控的延时,导致永远无法确保一定能得到完整的结果。这就需要系统有推理时间窗口完整性的能力, 需要系统即便数据迟到也能更新完善计算结果的能力。

数据处理模式

无限数据集的处理,往往离不开下面四个问题。

计算的结果是什么?(What results are calculated?)

流计算的结果由一个个转换(Transformation)组成。

可能的形式有:

  • 元素级别(Element-wise),如 Map
  • 聚合(Grouping),如 Sum
  • 组合(Compositing),如 Map + Sum

元素级别的转换和聚合的区别是,聚合是有状态的,要比无状态的元素级别转换复杂很多。下面的三个问题,都可以看作是针对聚合转换的。

在什么时间范围里计算?(Where in event time are results calculated?)

计算可以发生在窗口(Window)之中,窗口在时间域上将数据分成若干份。

常见的窗口形式:

  • 固定窗口
  • 滑动窗口
  • 会话窗口

何时输出计算结果?(When in processing time are results materialized?)

  • 触发器(Triggers)
  • 水位(Watermark)

何时输出计算结果?(When in processing time are results materialized?)

触发器(Triggers)控制结果输出。触发器可被下列信号触发:

  • Event Time
  • Processing Time
  • 窗口内事件的数量
  • 特殊的事件元素。

水位(Watermark)是衡量当前窗口是否完成的标志。简单来看,水位是 Processing Time 到 Event Time 的一个映射, F(P) -> E。 给定一个 Processing Time,即标志得到的 Event Time 之前的事件均已处理完毕。因此水位必须是单调增函数。

真实世界中,得到完美水位是几乎不可能的,通常会要采用启发式的方法。水位保证的是数据的完整性。

如何完善计算结果(How do refinements of results relate?)

有几种模式:

  • 抛弃(discarding)
  • 累积(accumulate)
  • 撤回并累计(accumulate + retraction)

完善结果需要存储的支持。

数据处理框架

数据处理框架做了什么?

  • 抽象的编程模型,提供语义保证。
  • 提供 Scalability。
  • Fault Tolerance。

现有的数据处理框架

Tyler在一次演讲中对现有的数据处理框架作了对比

Apache Flink

Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams

特点

  • Exactly-once state consistency
  • Event-time processing
  • Checkpoint & Savepoint
  • Queryable state store
  • Metrics
  • High Throughput & Low Latency

性能

  • 30 Machines, 120 cores
    throughput: 1.5 M events/sec, .99 latency 20ms
    throughput: 80 M events/sec, .99 latency 50ms \\

更多的问题

  • Window Join
  • Watermark propagation
  • Stream SQL
  • GC
  • Back Pressure
  • Parallelism

(我一个都不懂 -_-)

更多阅读