11. 流处理
有效的复杂系统总是从简单的系统演化而来。 反之亦然:从零设计的复杂系统没一个能有效工作的。
——约翰·加尔,Systemantics(1975)
在第10章中,我们讨论了批处理技术,它读取一组文件作为输入,并生成一组新的文件作为输出。输出是衍生数据(derived data)的一种形式;也就是说,如果需要,可以通过再次运行批处理过程来重新创建数据集。我们看到了如何使用这个简单而强大的想法来建立搜索索引,推荐系统,做分析等等。
然而,在第10章中仍然有一个很大的假设:即输入是有界的,即已知和有限的大小,所以批处理知道它何时完成输入的读取。例如,MapReduce核心的排序操作必须读取其全部输入,然后才能开始生成输出:可能发生这种情况:最后一条输入记录具有最小的键,因此需要第一个被输出,所以提早开始输出是不可行的。
实际上,很多数据是无界限的,因为它随着时间的推移而逐渐到达:你的用户在昨天和今天产生了数据,明天他们将继续产生更多的数据。除非你停业,否则这个过程永远都不会结束,所以数据集从来就不会以任何有意义的方式“完成”【1】。因此,批处理程序必须将数据人为地分成固定时间段的数据块,例如,在每天结束时处理一天的数据,或者在每小时结束时处理一小时的数据。
日常批处理中的问题是,输入的变更只会在一天之后的输出中反映出来,这对于许多急躁的用户来说太慢了。为了减少延迟,我们可以更频繁地运行处理 —— 比如说,在每秒钟的末尾 —— 或者甚至更连续一些,完全抛开固定的时间切片,当事件发生时就立即进行处理,这就是流处理(stream processing)背后的想法。
一般来说,“流”是指随着时间的推移逐渐可用的数据。这个概念出现在很多地方:Unix的stdin和stdout,编程语言(惰性列表)【2】,文件系统API(如Java的FileInputStream
),TCP连接,通过互联网传送音频和视频等等。
在本章中,我们将把事件流(event stream)视为一种数据管理机制:无界限,增量处理,与上一章中批量数据相对应。我们将首先讨论怎样表示、存储、通过网络传输流。在“数据库和流”中,我们将研究流和数据库之间的关系。最后在“流处理”中,我们将研究连续处理这些流的方法和工具,以及它们用于应用构建的方式。