引言
流式数据处理在如今的大数据领域是很重要的,无非就是以下这些原因:
- 商业公司更加渴望及时的数据,而切换成流式技术栈是达到更低的延时的一种方法
- 海量无边界的数据集合在现代商业公司里面都是越来越普遍,这些数据需要使用一个专业为无限数据设计的系统才能更好的为人所用
- 当数据在到达的那一刻,可以使系统负载随着时间更均匀的分布,从而保证更一致性和可预测的资源消耗。
尽管业务驱动对于流式数据的兴趣激增,但是与批处理流系统相比,现有的大多数流系统仍然相对不成熟,这导致了最近许多令人兴奋和活跃的发展在这个领域。作为已经在谷歌从事超大规模流式系统超过5+年的经验的人,至少可以说,我对这个流媒体时代精神感到高兴。
我也有兴趣确保人们了解流媒体系统能够做到的一切以及如何最好地使用它们,特别是考虑到大多数现有批处理和流媒体系统之间存在的语义差距。为此,O’Reilly的优秀人员邀请我为Strata + Hadoop World London 2015做一个版面文章,关于我的Say Goodbye to Batch的交流。
- Stream101: 第一篇文章将会介绍一些基本背景信息,并澄清一些术语,然后深入讨论时间域的细节,以及数据处理的常用方法(批处理和流处理)的高级概述。
- 数据流模型(The Dataflow Model):第二篇文章将主要由云数据流使用的统一批处理+流模型的快速浏览组成,通过一个跨不同用例集合应用的具体示例进行简化。之后,我将对现有的批处理和流式系统进行简单的语义比较。
所以,长篇大论的介绍,让我们开始。
背景
首先,我将介绍一些重要的背景信息,这些信息将有助于构建我想要讨论的其他主题。我们将分为以下三个特定部分:
术语:要准确地谈论复杂的主题,需要精确定义术语。对于某些在当前使用中有过多解释的术语,我会试着确切地指出我说的时候的意思。
能力:我将点评流式系统经常出现的缺点。我还提出了我认为数据处理系统构建者需要采用的思维方式,以满足现代数据消费者未来的需求。
时域:我将介绍与数据处理相关的两个主要时间域,显示它们之间的关系,并指出这两个域所带来的一些困难。
术语:什么是Streaming
在继续之前,我想先解决一件事:什么是Streaming?今天使用术语“Streaming”来表示各种不同的东西(为简单起见,我到目前为止一直使用它有点松散),这可能导致对Streaming实际上是什么或者Streaming System的实际能力的误解。因此,我更愿意在某种程度上精确地定义这个术语
问题的关键在于许多应该由它们所描述的东西(例如,无界数据处理,近似结果等),变成了它们在之前是如何完成来描述(即通过流式执行引擎)。这种缺乏精确性的术语会影响Streaming的实际意义,并且在某些情况下,会给StreamingSystem本身带来负担,暗示其功能仅限于经常被描述为Streaming的特征,例如近似或推测结果。鉴于精心设计的StreamingSystem与任何现有的批量引擎一样能够(技术上更加如此)产生正确,一致,可重复的结果,我更倾向于将术语Streaming分离为一个非常具体的含义:一种考虑了无限的数据集的数据处理引擎。仅此而已。(为了完整起见,或许值得一提的是,这个定义包括真正的流式和微批量实现。)
至于Streaming的其他常见用法,这里有一些我经常听到的,每一个都有更精确的描述性术语,我建议我们作为一个社区应该尝试采用:
- 无边界数据:一种不断增长的,基本上无限的数据集。这些通常被称为“流式数据”。然而,当应用于数据集时,术语Streaming或batch是有问题的,因为如上所述,它们意味着使用某种类型的执行引擎来处理那些数据集。实际上,所讨论的两类数据集之间的关键区别在于它们的有限性,因此最好用捕获这种区别的术语来表征它们。因此,我将无限“Streaming”数据集称为无界数据,将有限“batch”数据集称为有界数据。
- 无界数据处理:一种持续的数据处理模式,应用于上述类型的无界数据。尽管我个人喜欢使用术语Streaming来描述这种类型的数据处理,但在这种情况下它的使用再次意味着使用流执行引擎,这是具有误导性的;自批处理系统首次构思以来,已经使用批量引擎的重复运行来处理无界数据(相反,精心设计的流式传输系统能够处理超过有界数据的“批量”工作负载)。因此,为了清楚起见,我将简称为无界数据处理。
- 低延迟,近似和/或推测结果:这些类型的结果通常与流引擎相关联。批处理系统传统上没有考虑低延迟或推测结果的设计这一事实是一个历史人工制品,仅此而已。当然,如果指示,批量引擎完全能够产生近似结果。因此,与上述术语一样,将这些结果描述为它们(低延迟,近似和/或推测)比通过它们历史上如何表现(通过流引擎)更好。
从现在开始,每当我使用术语“Streaming”时,你可以放心地认为我的意思是为无界数据集设计的执行引擎,仅此而已。当我说上述任何其他术语时,就仅仅代表无界数据,无界数据处理或低延迟/近似/推测结果。这些是我们在Cloud Dataflow中采用的术语,我鼓励其他人采取类似的立场。
关于Streaming的被极度夸大的局限性
接下来,我们来谈谈Streaming Systems可以做什么和不能做什么,重点是可以做的事;在这些文章中,我想要介绍的最重要的事情之一就是精心设计的Streaming System的功能。Streaming Systems长期以来一直被用在提供低延迟,不准确/推测的结果的数据领域,通常与更强大的批处理系统相结合,以提供最终正确的结果,即Lambda架构。
对于那些尚未熟悉Lambda架构的人来说,基本的想法是在Batch System旁边运行Streaming System,两者都执行基本相同的计算。Streaming System提供低延迟,不准确的结果(或者由于使用近似算法,或者因为Streaming System本身不提供正确性),并且一段时间之后Batch System启动来为您提供正确的输出。最初由Twitter的Nathan Marz(Storm的创造者)提出,它最终取得了相当大的成功,因为事实上,它当时是一个很棒的主意;Streaming Engine在正确性上有点失望,Batch Engine本身就像你想的那样笨拙,所以Lambda给你一个方法来保证鱼和熊掌兼得。遗憾的是,维护Lambda系统很麻烦:您需要构建,提供和维护两个独立版本的数据管道,然后在某个时刻合并两个数据管道的结果。
作为一个多年来致力于强大的Streaming Engine的人,我也发现Lambda架构的整个原理有点令人讨厌。不出所料,我是Jay Kreps的文章Questioning the Lambda Architecture的忠实粉丝。这篇文章是曝光度比较高的反对双模执行必要性的文章之一;漂亮。 Kreps用像Kafka这样的可重放系统,在流互连的背景下解决了可重复性的问题,并且深入提出了Kappa架构,这基本上意味着使用一个精心设计的系统来运行一个数据管道,该系统适合于手头的工作。我不相信概念本身需要一个名字,但我原则上完全支持这个想法。
老实说,我会更进一步。我认为精心设计的Streaming System实际上提供了包含批处理功能的超集。Modulo perhaps an efficiency delta,看来应该不需要这些的Batch System。我要感谢Flink开发将这个想法铭记于心并构建了一个all-streaming-all-the-time的系统,即使在“Batch”模式下也是如此;我喜欢它。
所有这一切的必然结果是Streaming System的广泛成熟与无限数据处理的强大框架相结合,将Lambda架构比下去,将其变成大数据时代的历史。我相信现在是实现这一目标的时候了。为了在Batch擅长的领域打败Batch System,你仅仅只需要两件事:
- Correctness — 这次可以和Batch打成平手
正确性最核心的事可以归结为一致性存储。Streaming System需要一种方法来检查持久状态随着时间的推移(Kreps在他的“为什么本地状态是流处理后的基本原语”中已经讨论过了),并且它必须经过精心设计,足以在机器故障的情况下保持一致。几年前,当Spark Streaming首次出现在公共大数据场景中时,它是一个在其他黑暗流媒体世界中一致的灯塔。值得庆幸的是,从那时起情况有所改善,但是有多少Streaming System仍然试图在没有强大一致性的情;我真的不敢相信,最多一次处理仍然是一件事,但确实如此。(Thankfully, things have improved somewhat since then, but it is remarkable how many streaming systems still try to get by without strong consistency; I seriously cannot believe that at-most-once processing is still a thing, but it is.)
重申一下,因为这一点非常重要:一次性处理需要强一致性,这是正确性所必需的,这是任何系统想要达到或超过Batch Systems的必要条件。除非你真的不关心你的结果,否则我恳请你避开任何不提供强烈一致状态的Streaming System。Batch systems不要求您提前验证它们是否能够产生正确的答案;不要浪费你的时间在不能满足相同标准的Streaming System上。
如果您想了解更多关于在Streaming System中怎么获得强一致性所需的内容,我建议您查看MillWheel和Spark Streaming论文。两篇论文都花了大量时间讨论一致性。鉴于文献和其他地方有关该主题的高质量信息,我不会在这些帖子中进一步介绍。
- Tools for reasoning about time — 这次将完胜Batch
优秀的时间推理工具对于处理有事件时间偏差的无界、无序数据至关重要。越来越多的现代数据集具有这些特征,现有的batch systems(以及大多数streaming systems)缺乏必要的工具来应对它们所带来的困难。我将花费这篇文章的剩余部分,以及下一篇文章的大部分内容,解释并关注这一点。
首先,我们要对时域的重要概念有一个基本的了解,之后我们将深入研究不同事件时间偏差的无界无序数据的含义。然后,本文的其余部分将来探讨Batch&Streaming System的有界和无界数据处理的常用方法。
Event time vs. processing time
要谈论无限数据的处理,需要清楚地了解所涉及的时间领域。在任何数据处理系统中,通常有两个我们关心的时间域:
- Event time, 事件实际发生的时间。
- Processing time,这是在Streaming System中观察事件的时间
并非所有的Case都关心事件时间(如果你的事情没有,那么万岁! - 你的生活更容易),但很多人都关心这个。比如说包括表征用户随时间的行为,大多数计费应用程序以及许多类型的异常检测,不一而足。
在理想的世界中,事件时间和处理时间总是相等的,事件在发生时立即处理。然而,现实并非如此,事件时间和处理时间之间的偏差不仅非零,而且通常是底层输入源,执行引擎和硬件特性的高度可变函数。可能影响偏差程度的事情包括: - 共享资源限制,例如网络拥塞,网络分区或非专用环境中的共享CPU。
- 软件原因,例如分布式系统逻辑,临界区等。
- 数据本身的特征,包括密钥分布,吞吐量的变化或无序的变化(例如,在离线使用整个航班之后,人们将他们的电话带出飞机模式的飞机)。
因此,如果您在任何实际系统中绘制事件时间和处理时间的进度,通常会得到一些看起来有点像图1中的红线的东西。
斜率为1的黑色虚线表示理想值,其中处理时间和事件时间完全相等;红线代表现实。在这个例子中,系统在处理时间开始时滞后一点,在中间偏向理想状态,然后在结束时稍微滞后一点。理想线和红线之间的水平距离是处理时间和事件时间之间的偏差。这种偏差本质上是处理流水线引入的延迟。
由于事件时间和处理时间之间的映射不是静态的,这意味着如果您关心事件时间(即事件实际发生的时间),则无法仅在管道中观察数据的上下文中分析数据。不幸的是,这是大多数现有系统为无界数据设计的方式。为了应对无界数据集的无限性,这些系统通常提供一些窗口化输入数据的概念。我们将在下面深入讨论窗口,但它实质上意味着将数据集沿时间边界切割成有限的片段。
如果您关心正确性并且有兴趣在事件时间的上下文中分析您的数据,则无法像大多数现有系统那样使用处理时间(即处理时间窗口)来定义这些时间边界;在处理时间和事件时间之间没有一致的相关性,你的一些事件时间数据将在错误的处理时间窗口中结束(由于分布式系统的固有滞后,许多类型的输入源的在线/离线性质,等等,把正确性扔出窗外。我们将在下面的一些示例以及下一篇文章中更详细地研究这个问题。
不幸的是,当按事件时间窗口化时,图片并不完全乐观。在无界数据的情况下,无序和变量偏斜会导致事件时间窗口的完整性问题:缺少处理时间和事件时间之间的可预测映射,如何确定何时观察到给定事件时间X的所有数据?对于许多真实世界的数据源,你根本做不到。当今使用的绝大多数数据处理系统都依赖于某些完整性概念,这使得它们在应用于无界数据集时处于严重劣势。
我建议不要试图将无限数据整理成最终变得完整的有限批次信息,而应该设计工具,让我们生活在这些复杂数据集所带来的不确定性的世界中。新数据将到来,旧数据可能会被撤回或更新,我们构建的任何系统都应该能够自己处理这些事实,完整性概念是一种方便的优化而不是语义必需。
在深入探讨如何使用Cloud Dataflow中使用的数据流模型构建此类系统之前,让我们完成一个更有用的背景:常见的数据处理模式。
Data processing patterns
目前为止,我们已经建立了足够的背景,我们可以开始研究当今有界和无界数据处理中常见的核心类型的使用模式。我们将在我们关注的两种主要类型的引擎的上下文中查看这两种类型的处理(相关的)(批处理和流式传输,在这种情况下,我基本上将微批处理与流式传输混合起来,因为两者之间的差异在这个层面上并不十分重要)。
Bounded data
处理有界数据非常简单,并且可能对每个人都很熟悉。在下图中,我们从左侧开始,数据集充满了熵。我们通过一些数据处理引擎(通常是批处理,虽然设计良好的流引擎也能正常工作)运行它,例如MapReduce,并在右端使用具有更大内在价值的新结构化数据集:
当然,作为该方案的一部分,您可以实际计算的内容存在无限变化,但整体模型非常简单。更有趣的是处理无界数据集的任务。现在让我们看看通常处理无限数据的各种方式,从传统批处理引擎使用的方法开始,然后结束可以采用专为无界数据设计的系统的方法,例如大多数流媒体或微批处理引擎。
Unbounded data — batch
批量引擎虽然没有明确地设计为无限数据,但已被用于处理无界数据集,因为批量系统最初是在构思之前。正如人们所预料的那样,这种方法围绕着将无界数据切割成适合批处理的有界数据集的集合。
Fixed windows
使用批处理引擎的重复运行来处理无界数据集的最常用方法是将输入数据窗口化为固定大小的窗口,然后将每个窗口作为单独的有界数据源处理。特别是对于像日志这样的输入源,其中事件可以写入目录和文件层次结构,其名称编码它们对应的窗口,这种事情在乍一看似乎非常简单,因为你基本上执行了基于时间的随机数来获取数据提前进入适当的事件时间窗口。
但实际上,大多数系统仍然存在完整性问题需要处理:如果由于网络分区而导致某些事件延迟到达日志,该怎么办?如果您的活动是全球收集的,并且必须在处理之前转移到公共位置,该怎么办?如果您的活动来自移动设备怎么办?这意味着可能需要某种缓解(例如,在您确定已收集所有事件之前延迟处理,或者在数据迟到时重新处理给定窗口的整个批处理)。
Sessions
当您尝试使用批处理引擎将无限数据处理为更复杂的窗口策略(如会话)时,此方法会更加崩溃。会话通常被定义为由不活动间隙终止的活动时段(例如,对于特定用户)。使用典型的批处理引擎计算会话时,您经常会遇到按批次分割的会话,如下图中的红色标记所示。可以通过增加批量大小来减少拆分数量,但代价是延迟增加。另一种选择是添加额外的逻辑来拼接先前运行的会话,但代价是进一步复杂化。
无论哪种方式,使用经典的批处理引擎来计算会话都不太理想。一种更好的方法是以流式方式构建会话,稍后我们将对此进行介绍。
Unbounded data — streaming
与大多数基于批处理的无界数据处理方法的临时性质相反,流式系统是针对无界数据构建的。正如我前面提到的,对于许多真实的分布式输入源,您不仅会发现自己处理无界数据,还会处理以下数据:
- Highly unordered with respect to event times,这意味着如果要在发生它们的上下文中分析数据,则需要在管道中进行某种基于时间的混洗。
- Of varying event time skew, 这意味着你不能只假设你总能看到给定事件时间X的大部分数据在某个恒定的时间ε之内。
在处理具有这些特征的数据时,可以采取一些方法。我通常将这些方法分为四组:
- Time-agnostic
- Approximation
- Windowing by processing time
- Windowing by event time
我们现在花一点时间来研究这些方法。
Time-agnostic
在时间基本上不相关的情况下使用时间不可知处理 - 即,所有相关逻辑都是数据驱动的。由于有关此类使用案例的所有内容都是由更多数据的到来决定的,因此除了基本数据传输之外,流媒体引擎实际上没有什么特别需要支持的。因此,基本上所有流媒体系统都支持开箱即用的时间不可用的用例(一致性保证系统到系统的差异,当然,对于那些关心正确性的人来说)。批处理系统也非常适用于无界数据源的时间不可知处理,只需将无界源切割成有界数据集的任意序列并独立处理这些数据集。我们将在本节中看一些具体的例子,但考虑到处理与时间无关的处理的直接性,除此之外不会花费太多时间。
Filtering
时间不可知处理的一种非常基本的形式是过滤。想象一下,您正在处理Web流量日志,并且您想要过滤掉所有不是来自特定域的流量。你会看到每个记录到达时,查看它是否属于感兴趣的域,如果没有则删除它。由于这种事情在任何时候都只依赖于单个元素,因此数据源无限制,无序且事件时间偏差变化的事实无关紧要。
Inner-joins
另一个与时间无关的例子是内连接(或哈希连接)。当连接两个无界数据源时,如果您只关心来自两个源的元素到达时的连接结果,则逻辑上没有时间元素。在看到来自一个源的值后,您可以简单地将其缓冲为持久状态;一旦来自另一个源的第二个值到达,您只需要发出连接记录。 (事实上,你可能想要某种垃圾收集策略用于未经许可的部分连接,这可能是基于时间的。但是对于很少或没有未完成连接的用例,这样的事情可能不是问题。)
将语义切换到某种外连接会引入我们所讨论的数据完整性问题:一旦你看到了连接的一面,你怎么知道另一面是否会到达?说实话,你没有,所以你必须引入一些超时的概念,这会引入一个时间元素。时间元素本质上是一种窗口形式,我们将在一瞬间更加密切地关注它。
Approximation algorithms
第二个主要类别的方法是近似算法,例如近似的前N个,流式K-means等。它们采用无限的输入源并提供输出数据,如果你眯着眼睛看,它们看起来或多或少像你的那样我希望得到。近似算法的优点在于,通过设计,它们的开销很低,并且设计用于无界数据。缺点是它们存在有限的一组,算法本身往往很复杂(这使得很难想出新的算法),它们的近似性质限制了它们的效用。
值得注意的是:这些算法通常在其设计中具有一些时间元素(例如,某种内置衰变)。并且由于它们在到达时处理元素,因此该时间元素通常基于处理时间。这对于在其近似上提供某种可证明的误差界限的算法尤其重要。如果这些误差范围是以按顺序到达的数据为基础的,那么当您向算法提供具有不同事件时间偏差的无序数据时,它们基本上没有任何意义。要记住的事情。
近似算法本身是一个引人入胜的主题,但由于它们本质上是时间不可知处理的另一个例子(以算法本身的时间特征为模),它们使用起来非常简单,因此在我们目前的重点下不值得进一步关注。
Windowing
其余两种无限制数据处理方法都是窗口的变化。在深入研究它们之间的差异之前,我应该清楚地说明窗口的确切含义,因为我只是简单地触及了它。窗口化只是获取数据源(无界或有界)的概念,并沿着时间边界将其切割成有限的块以进行处理。下图显示了三种不同的窗口模式:
Fixed windows
固定窗口将时间切割成具有固定大小的时间长度的段。通常(如图8所示),固定窗口的段均匀地应用于整个数据集,这是对齐窗口的示例。在某些情况下,希望对不同数据子集(例如,每个键)的窗口进行相移以随时间更均匀地扩展窗口完成负载,而不是未对齐窗口的示例,因为它们在数据上变化。
Sliding windows
固定窗口的推广,滑动窗口由固定长度和固定周期定义。如果周期小于长度,则窗口重叠。如果周期等于长度,则您有固定的窗口。如果周期大于长度,则会有一种奇怪的采样窗口,它只会随着时间的推移查看数据的子集。与固定窗口一样,滑动窗口通常是对齐的,但在某些用例中可能不对齐作为性能优化。注意,图8中的滑动窗口是按原样绘制的,以给出滑动感;实际上,所有五个窗口都将应用于整个数据集。
Sessions
动态窗口的一个例子,会话由大于某个超时的不活动间隙终止的事件序列组成。会话通常用于通过将一系列与时间相关的事件(例如,在一次观看中观看的一系列视频)组合在一起来分析用户随时间的行为。会话很有意思,因为它们的长度不能先验地定义;它们取决于所涉及的实际数据。它们也是未对齐窗口的规范示例,因为会话实际上在不同的数据子集(例如,不同用户)之间从不相同。
讨论的两个时间域 - 处理时间和事件时间 - 基本上是我们关心的两个[2]。窗口化在两个域中都有意义,因此我们将详细查看每个窗口并查看它们之间的区别。由于处理时间窗口在现有系统中非常普遍,我将从那里开始。
Windowing by processing time
当按处理时间窗口化时,系统基本上将输入数据缓冲到窗口中,直到经过一定量的处理时间。例如,在五分钟固定窗口的情况下,系统会将数据缓冲五分钟的处理时间,之后它会将这五分钟内观察到的所有数据视为一个窗口并将它们发送到下游进行处理。
处理时间窗口有一些很好的特性:
- 简单,由于您从不担心在一段时间内改变数据,因此实现非常简单。你只需在它们到达时缓冲它们并在窗口关闭时将它们发送到下游。
- 判断窗口的完整性很简单。由于系统完全了解是否已经看到窗口的所有输入,因此可以就给定窗口是否完整做出完美的决定。这意味着当按处理时间进行窗口化时,无需以任何方式处理“延迟”数据。
- 如果您想要在观察时推断出有关源的信息,那么处理时间窗口正是您想要的。许多监测方案都属于这一类。想象一下,跟踪发送到全球规模Web服务的每秒请求数。为了检测中断而计算这些请求的速率是处理时间窗口的完美使用。
除了优点之外,处理时间窗口有一个非常大的缺点:如果有问题的数据具有与它们相关联的事件时间,那么如果处理时间窗口要反映实际上这些事件的实际情况,那么这些数据必须按事件时间顺序到达发生了。不幸的是,在许多真实的分布式输入源中,事件时序数据并不常见。
举一个简单的例子,想象一下任何收集使用统计数据以供以后处理的移动应用程序。如果给定的移动设备在任何时间内脱机(短暂的连接丢失,飞越全国时的飞行模式等),则在该设备再次上线之前不会上传在此期间记录的数据。这意味着数据可能会以分钟,小时,天,周或更长的事件时间偏差到达。当处理时间窗口化时,基本上不可能从这样的数据集中得出任何有用的推论。
作为另一个例子,当整个系统健康时,许多分布式输入源似乎可以提供事件时间有序(或非常接近)的数据。不幸的是,健康时事件时间偏差对于输入源来说是低的这一事实并不意味着它总会保持这种状态。考虑一个处理在多个大洲收集的数据的全球服务。如果网络问题跨越带宽受限的跨大陆线路(遗憾的是,这种情况非常常见)会进一步降低带宽和/或增加延迟,突然一部分输入数据可能会以比以前更大的偏差开始到达。如果您通过处理时间来处理该数据,那么您的窗口不再代表其中实际发生的数据;相反,当事件到达处理管道时,它们代表时间窗口,这是旧数据和当前数据的任意混合。
我们在这两种情况下真正想要的是以一种对事件到达顺序稳健的方式按事件时间窗口数据。我们真正想要的是事件时间窗口。
Windowing by event time
当您需要观察有限块中反映事件实际发生时间的数据源时,可以使用事件时间窗口。这是开窗的黄金标准。遗憾的是,目前使用的大多数数据处理系统都缺乏对它的原生支持(尽管任何具有良好一致性模型的系统,如Hadoop或Spark Streaming,都可以作为构建这种窗口系统的合理基础)。
此图显示了将无界源窗口化为一小时固定窗口的示例:
图中的实线白线表示两个特别感兴趣的数据。这两个数据都到达处理时间窗口,这些时间窗口与它们所属的事件时间窗口不匹配。因此,如果这些数据已被窗口化为处理关注事件时间的用例的处理时间窗口,则计算结果将是不正确的。正如人们所料,事件时间正确性是使用事件时间窗口的一个好处。
关于无界数据源的事件时间窗口的另一个好处是,您可以创建动态大小的窗口,例如会话,而不会在通过固定窗口生成会话时观察到任意分割(正如我们之前在“无界数据”中的会话示例中看到的那样) - 批次“部分”:
当然,强大的语义很少是免费的,事件时间窗也不例外。事件时间窗口有两个显着的缺点,因为窗口必须经常比窗口本身的实际长度更长(处理时间):
- 缓冲: 由于延长了窗口寿命,因此需要更多的数据缓冲。值得庆幸的是,持久存储通常是大多数数据处理系统所依赖的资源类型中最便宜的(其他类型主要是CPU,网络带宽和RAM)。因此,当使用具有强一致性持久状态和体面内存缓存层的任何精心设计的数据处理系统时,这个问题通常比人们想象的要少得多。而且,许多有用的聚合不要求整个输入集被缓冲(例如,总和或平均),而是可以递增地执行,其中存储在持久状态中的小得多的中间聚合。
- 完整性:鉴于我们通常没有很好的方法知道我们何时看到给定窗口的所有数据,我们如何知道窗口的结果何时可以实现?事实上,我们根本就没有。对于许多类型的输入,系统可以通过类似MillWheel的水印(我将在第2部分中详细讨论)提供合理准确的窗口完成启发式估计。但是在绝对正确性至关重要的情况下(再次考虑计费),唯一真正的选择是为管道构建者提供一种方式来表达何时需要实现窗口的结果,以及如何随着时间的推移改进这些结果。处理窗口完整性(或缺少窗口完整性)是一个引人入胜的主题,但也许最好在具体示例的背景下进行探讨,我们将在下一次讨论。
结论
呼!那是很多信息。对于那些已经做到这一点的人:你将受到赞扬!在这一点上,我们大致已经完成了我想要覆盖的材料的一半,所以回过头来回顾一下我到目前为止所涵盖的内容可能是合理的,并且在进入第2部分之前让事情稍微解决。所有这些的好处是第1部分是无聊的职位;第2部分是真正开始的乐趣所在。