flink 学习 (1)Streaming 101: 批处理之上的世界

235 阅读35分钟

流数据处理的起源场景

现在的业务场景中,大数据处理中的海量数据处理已经日渐成为一大难题,理由如下:

  • 商业公司以及企业渴望获得更加及时的数据,而将现有的批处理场景切换到流处理是一种实现更低延迟的好办法。

  • 设计一种新的专为处理无止境的数据量而生的系统,可以更好的承载新业务场景中越来越普遍的海量,无限制的数据集。

  • 在数据到达以后再进行处理,可以让工作负载均匀的分布在时间片上,从而让资源消耗更加一致和可预测。

    尽管商业模型对流处理系统产生了极大的兴趣,但是与成熟的批处理系统相比,流处理的新生力量还不够成熟和稳定,但是还是有很多的让人兴奋的,积极的发展。 作为过去五年多在 Google 从事大规模流处理系统(MillWheel、Cloud Dataflow)工作的人,至少可以说,我对这种流处理时代精神感到高兴。我也有兴趣和人们分享流处理系统的所有能力以及如何最好地使用它们,特别是考虑到现在大多数现批处理和流处理系统之间仍然存在巨大的语义鸿沟。 为此,O'Reilly 邀请我贡献我在 Strata + Hadoop World London 2015 上的 Say Goodbye to Batch 演讲的书面演绎。由于我有很多需要阐述的内容,我将把它拆分为 两个单独的帖子:

  1. Streaming 101:第一篇文章将介绍一些基本的背景信息并解释一些术语,然后再深入的阐述有关时域的详细信息以及对批处理和流数据处理的常用方法的高度的概述。
  2. Dataflow 模型:第二篇文章将主要包括对 Cloud Dataflow 使用的统一批处理 + 流模型的统一介绍,并通过一个应用于不同场景的具体示例来促进。 最后,将对现有批处理和流处理系统的简要语义进行比较叙述。 闲话少说,下面进入真正的主题:

背景

首先,我会先介绍一些重要的背景信息帮助理解接下来讨论的主题,会有三个部分:

  • 术语:要准确地谈论复杂的话题,必需对术语进行精确的定义。 对于一些在当前环境中拥有过多解释的术语,我会尽量准确地确定它在文章中的含义。
  • 功能:我将评论流处理系统的常见缺点。 我还将提出我认为数据处理系统构建者需要采用的思维框架,以满足现代数据处理系统未来的需求。
  • 时域:我将介绍与数据处理相关的两个主要时间域,展示它们之间的互相关系,并指出这两个域带来的一些困难。

术语:什么是流

在继续之前,我想先解决一件事:什么是流? “流”这个词今天用来表示各种不同的东西,这可能会导致对流到底是什么或流系统实际上是什么产生误解。 因此,我更想稍微精确地定义该术语。 问题的关键是,许多的事情应该用它们是什么来描述(例如,无界数据处理、近似结果等),经常通过它们在历史上是如何完成的来介绍(例如,通过流执行引擎)。术语上的这种缺乏精确性掩盖了流的真正含义,并且在某些情况下,流系统本身的能力被局限于“流”的特征,例如近似或推测结果。鉴于设计良好的流系统与任何现有的批处理引擎一样能够(在技术上更是如此)产生正确、一致、可重复的结果,我更愿意将术语流解释为一个非常具体的含义:一种数据处理引擎设计时兼顾到了无限数据集的处理需求而已。 (为了完整的说明就,这个定义也包括真正的流和微批处理实现方式。)

作为流的其他常见使用场景,下面我经常了解到的一些流的用法,每种用法都让我们描述流的术语更加的准确,我建议我们作为一个社区应该尝试采用这些描述:

  1. 无界数据:一种不断增长的,本质上是无限的数据集。这些通常被称为“数据流”。然而,应用于数据集时,流或批处理作为术语是有问题的,因为如上所述,它们意味着必须使用某种类型的执行引擎来处理这些数据集。实际上,所讨论的两种类型的数据集之间的主要区别在于它们的有限性,因此最好使用这种区别他们特征的术语。因此,我将无限“流”数据集称为无界数据,将有限“批量”数据集称为有界数据。
  2. 无界数据处理:一种持续的数据处理模式,适用于上述类型的无界数据。尽管我个人喜欢使用流这个术语来描述这种类型的数据处理,但它在这种情况下使用会意味着使用“流执行引擎”,这就是一种误导;自从首次构思批处理系统以来,批处理引擎的重复运行已被用于处理无界数据(相反,设计良好的流系统也能够更好的处理有界数据)。为了清楚起见,我将简单地将其称为无界数据处理。
  3. 低延迟、近似和/或推测结果:这些类型的结果最常与流引擎相关联。批处理系统的设计传统上并未考虑到低延迟或推测结果,这只是个历史原因。当然,如果必要的话批处理引擎完全能够产生近似结果。因此,与上面的术语一样,将这些术语描述为它们的本质(低延迟、近似和/或推测)比通过它们历史上的实现方式(通过流引擎)要好得多。

每当我使用“流式传输”这个术语时,您都可以放心地假设我指的是为无界数据集设计的执行引擎 当我指上述任何其他术语时,我将明确表示无界数据、无界数据处理或低延迟/近似/推测结果。 这些是我们在 Cloud Dataflow 中采用的术语,我也鼓励其他人采取相似的描述。

流处理被大大夸大的局限性

接下来,我们来谈谈流处理系统能做什么和不能做什么; 我想在这些帖子中表达的最重要的事情之一就是精心设计的流数据处理系统的能力。 流处理系统系统长期以来一直被归为一个提供低延迟、不准确/推测结果的小众场景,通常与功能更强大的批处理系统结合使用以提供最终正确的结果,即the Lambda 架构.

对于那些还不熟悉 Lambda 架构的人来说,这一方式基本上是在批处理系统旁边运行流系统,然后两者执行基本相同的计算。流系统提供低延迟、不准确的结果,一段时间后,批处理系统会启动并提供正确的输出。最初由 Twitter 的 Nathan Marz(Storm 的创建者)提出,最终获得了相当成功,因为它实际上在当时是一个绝妙的主意;流引擎在当时正确性方面有点令人失望,而批处理引擎与预期的一样笨拙,因此 Lambda 提供了一种方法来处理这样的场景和需求。但是,维护 Lambda 系统很麻烦:需要构建、配置和维护管道的两个独立版本,然后最后还要以某种方式合并来自两个管道的结果。

作为一个在强一致性流引擎上工作多年的人,我有点讨厌Lambda 架构的整个模式。同时我也是Jay Kreps 发表的《Questioning the Lambda Architecture》一文的忠实粉丝。 这是反对双模式执行必要性的第一个有力的陈述。 Kreps 在使用像 Kafka 这样的可重播系统作为解决了可重复性问题,甚至提出了 Kappa 架构,这基本上意味着可以使用设计良好的系统运行单个管道来支持我们的工作。 虽然我不觉得概念本身需要一个名字,但我原则上完全支持他的想法。 老实说我的想法比他更进一步。 我认为精心设计的流处理系统实际上提供了批处理功能的严格超集。 感谢 Flink 的人将这个想法铭记于心并构建了一个系统,即使在“批处理”模式下,它也能在背后一直使用流模式运行。 所有这一切的必然结果是,广泛成熟的流处理系统的与强大的无界数据处理框架相结合,最终会将Lambda 架构降级为历史。 我相信现在是实现这一目标的时候了。 因为击败批处理,真的只需要两件事:

  • 正确性——这可以让流处理和批处理达到相同等级。首先,正确性总结为一致的存储。流系统需要一种随时间检查点来存储持久状态的方法(Kreps 在他的“为什么本地状态是流处理中的基本原语”一文中谈到了这一点),并且它必须设计得足够好,以便在机器故障时也能保持一致。几年前,当 Spark Streaming 首次出现在公共大数据场景中时,它是一个在原本黑暗的流处理世界中保持一致性的灯塔。值得庆幸的是,现在情况以及有所好转,但是,有许多流处理系统仍然试图在没有强一致性的情况下勉强维持下去;我真的不敢相信最多一次处理对于很多系统仍然十个问题,但它确实如此。重申一下,因为这一点很重要:仅仅一次的处理非常需要强一致性,这是所必需的正确性,这是任何有机会达到或超过批处理系统能力的系统的要求。除非你真的不关心你的结果,否则我恳求你避开任何不提供强一致性状态的流处理系统。批处理系统不需要您提前验证它们是否能够产生正确的答案;不要在无法满足相同标准的流处理系统上浪费时间。如果想了解更多关于在流系统中获得强一致性需要什么,我建议您查看 MillWheel 和 Spark Streaming 论文。两篇论文都花了大量时间讨论一致性。鉴于这些论文和其他地方有关该主题的大量高质量信息,我不会在这个文章中进一步介绍。
  • 对于时间推理的工具——这让你超越了批处理。良好的时间推理工具对于处理不同事件时间偏差的无界、无序数据至关重要。 越来越多的现代数据集表现出这些特征,而现有的批处理系统(以及大多数流系统)缺乏必要的工具来应对它们带来的困难。 我将用这篇文章的剩余部分以及下一篇文章的大部分内容来解释和关注这一点。 首先,我们将对时域的重要概念有一个基本的了解,然后我们将更深入地了解我所说的具有不同事件时间偏差的无界、无序数据的含义。 然后,我们将在本文的其余部分研究使用批处理和流系统进行有界和无界数据处理的常见方法。

事件时间与处理时间

要令人信服地谈论无界数据处理,需要对所涉及的时间领域有清晰的了解。 在任何数据处理系统中,我们通常关心两个时间域:

  • 事件时间,即事件实际发生的时间。
  • 处理时间,即在系统中观察到事件的时间。

并非所有场景都关心事件时间(如果你不关心,万岁! - 你的生活更轻松),但大部分时间都需要。示例包括随时间刻画用户行为画像、大多数计费应用程序和许多类型的异常检测,等等。

在理想的世界中,事件时间和处理时间总是相等的,事件在发生时立即处理。然而,现实并非如此,事件时间和处理时间之间的偏差不仅非零,而且通常是底层输入源、执行引擎和硬件特性的高度可变函数。可能影响偏斜程度的因素包括:

共享资源限制,例如网络拥塞、网络分区或非专用环境中的共享 CPU。 软件原因,如分布式系统逻辑、争用等。 数据本身的特征,包括密钥分布、吞吐量变化或无序变化(例如,飞机上满是人,在整个飞行过程中离线使用手机后,他们将手机退出飞行模式的时候)。 因此,如果您在任何真实世界的系统中绘制事件时间和处理时间的进度,通常最终会得到类似于图 1 中的红线的结果。

image.png

图 1:时域映射示例。 X 轴表示系统中的事件时间完整性,即事件时间中的时间 X,直到事件时间小于 X 的所有数据都已被观察到。 Y 轴表示处理时间的进展,即数据处理系统在执行时观察到的正常时钟时间。

斜率为 1 的黑色虚线代表理想情况,其中处理时间和事件时间完全相等;红线代表现实。在这个例子中,系统在处理时间的开始有点滞后,在中间更接近理想状态,然后在接近结束时再次滞后。理想和红线之间的水平距离是处理时间和事件时间之间的偏差。这种偏斜本质上是处理管道带来的延迟。

由于事件时间和处理时间之间的映射不是静态的,这意味着如果关心它们的事件时间(即事件实际发生的时间),则不能仅依靠管道中观察到它们的时间来分析您的数据。不幸的是,现在大部分无界数据处理系统都是依靠时间来设计的。为了应对无界数据集的无限性质,这些系统通常提供一些对传入数据进行窗口化的概念。我们将在下面深入讨论窗口化,但它本质上意味着沿着时间边界将数据集分成有限的部分。 如果关心正确性并且有兴趣基于其事件时间分析您的数据,则不能像大多数现有系统那样使用处理时间(即处理时间窗口)定义这些时间边界;由于处理时间和事件时间之间没有一致的相关性,某些事件时间数据最终会出现在错误的处理时间窗口中(由于分布式系统的固有滞后、许多类型输入源的在线/离线特性,等),导致了上图一样的错误结果。我们将在下面的一些示例以及下一篇文章中更详细地研究这个问题。 但是,当按事件时间进行窗口化时,情况也不完全乐观。在事件时间窗口中,无序和变量偏斜会导致事件时间窗口的完整性问题:缺乏处理时间和事件时间之间的可预测映射,那么给定一个时间x,如何确定所有数据都在这个时间被观察完毕?对于许多现实世界的数据源,是根本做不到这样的预测的。当今使用的绝大多数数据处理系统都依赖于某种完整性概念,这使得它们在应用于无界数据集时处于严重劣势。

我建议与其尝试将无界数据整理成最终变得完整的有限信息批次,我们更应该设计工具,让我们能够把生活中这些不确定性的复杂数据集。新数据会提前到来,旧数据可能会被收回或更新,我们构建的任何系统都应该能够自己处理这些困难,完整性的概念是一种方便的优化而不是设计系统上的必要性。

在深入探讨如何尝试使用 Cloud Dataflow 中使用的 Dataflow 模型构建这样一个系统之前,让我们先了解一个更有用的背景:常见数据处理模式。

数据处理模式

此时我们已经准备了足够的基础知识,可以开始研究当今有界和无界数据处理中常见核心类型的处理模式。 我们将在我们关心的两种主要引擎类型(指批处理和流处理,在这里基本上将微批处理与流处理同样的讨论,因为在这个层级上,两者之间的差异并不是大)。

有界数据

处理有界数据是非常简单的,可能每个人都很熟悉。 在下图中,我们从左侧开始计算一个混乱无序的数据集。 我们通过一些数据处理引擎(通常是批处理,但设计良好的流引擎也能正常工作)运算它,例如 MapReduce,并在右侧最终得到一个具有更具有价值的新结构化数据集:

image.png

图示2 一个典型的批处理引擎处理有界数据的例子,左侧的混乱无序数据的有限池通过数据处理引擎运行,从而在右侧产生正确的结构化数据。

当然,实际上在这个方案的运作中,您可以使实际计算的内容和结果有无限的变化,但整体模型还是非常简单的。 更有趣的是如何去处理无界数据集。 现在让我们看看无界数据通常处理的各种方式,从传统批处理引擎使用的方法开始,然后是为无界数据设计的系统可以采用的方法,例如大多数流或微批处理引擎 .

无界数据-批处理

批处理引擎虽然没有在明确设计时考虑到如何无界数据,但自从第一次使用批处理系统以来,它实际上一直在被用于处理无界数据集。 正如人们所料,这些方法都围绕着将无界数据分割成适合批处理的有界数据集的集合来展开。

固定窗口

使用批处理引擎的重复运行来处理无界数据集的最常见方法是将输入数据集窗口化到固定大小的窗口中,然后将这些窗口中的每一个作为单独的有界数据源进行处理。特别是对于像日志这样的输入源,事件可以写入目录和文件层次结构,其名称对它们对应的窗口进行编码,这种事情乍一看似乎很简单,因为实际上已经采用了基于时间的shuffle(重新处理) 来获取数据进入适当的事件时间窗口。 实际上,大多数系统仍然存在一个完整性的问题需要处理:如果某些事件由于网络分区而延迟到达怎么办?如果事件在全球范围内收集并且必须在处理前传输到公共位置怎么办?如果活动来自移动设备怎么办?这意味着可能需要某种缓解措施(例如,延迟处理直到确定所有事件都已收集完毕,或者在数据延迟到达时为给定窗口重新处理整个批次)。

image.png

图示3 使用批处理引擎通过临时固定窗口进行无界数据处理。 一个无界数据集被预先收集到有限的、固定大小的有界数据窗口中,然后通过批处理引擎连续进行处理。

会话

当您尝试使用批处理引擎将无界数据处理为更复杂的窗口策略(如会话)时,这种方法会更加让人崩溃。会话窗口通常被定义为一个活动周期,超过一定时间不再活动就认为会话窗口中止。当使用批处理引擎计算会话窗口时,通常会遇到会话的数据拆分在2个或多个批次中的情况,如下图中的红色标记所示。可以通过增加每批次数据量的大小来减少拆分数量,但是代价是增加了延迟。另一个选择是增加额外的逻辑,从之前的运行中合并会话,但带来了更高的复杂度

image.png

图示4 使用批处理引擎通过临时固定窗口将无限数据处理到会话中。 无界数据集预先收集到有限、固定大小的有界数据窗口中,然后通过批处理引擎的连续处理将其细分为动态会话窗口。

无论哪种方式,使用经典的批处理引擎来计算会话都不是理想的方式。 一个更好的方法是以流方式建立会话,接下来我们会介绍这种方式。

无界数据-流处理

与大多数基于批处理的无界数据处理方法的临时性质相反,流系统是为无界数据构建的。 正如我之前提到的,对于许多现实世界的分布式输入源,不仅要处理无界数据,还要处理以下数据: 高度无序的事件时间,这意味着如果想在数据发生的上下文中分析数据,则需要在管道中进行某种基于时间的shuffle。 多种多样的事件时间偏差,这意味着不能只是假设将始终在时间 Y 的某个常数 epsilon 内看到给定事件时间 X 的大部分数据。

在处理具有这些特征的数据时,可以采用几种方法。 我通常将这些方法分为四组:

  • 时间无关
  • 近似值
  • 按处理时间的窗口
  • 按事件时间的窗口 接下来我们将花一点时间研究这些方法中的每一种。

Time-agnostic 时间无关

时间无关处理用于时间本质上无关的场景——即所有相关逻辑都是数据驱动的。由于此类用例的一切都取决于更多数据的到来,因此除了基本数据传输之外,流引擎实际上没有什么特别需要支持的。因此,基本上所有现有的流处理系统都支持开箱即用的时间无关用例(当然,对于那些关心正确性的人来说,一致性保证了系统到系统之间差异不存在)。批处理系统也非常适合无界数据源的时间无关处理,通过简单地将无界源切割成有界数据集的任意序列并独立处理这些数据集。我们将在本节中查看几个具体示例,但鉴于处理时间无关处理的简单性,除此之外我们不会花太多时间在上面。

Filtering 过滤

时间不可知处理的一种非常基本的形式是过滤。假设您正在处理 Web 流量日志,并且想要过滤掉并非来自特定域的所有流量。您会在每条记录到达时查看它,看看它是否属于需要的域,如果不属于,则将其删除。由于这种事情在任何时候都只依赖于单个元素,因此数据源是无界、无序或者不同事件时间倾斜的是无关紧要的。

image.png

图 5:过滤无界数据。 不同类型的数据集合(从左到右流动)被过滤成一个包含单一类型的同构集合。

Inner-joins 内连接

另外一个时间无关的例子是内连接的类型(或者哈希连接),当两个无界数据互相连接,如果你只关心两个数据同时达到时候生成的结果,则在处理逻辑中并没有时间元素。其中一个源的数据到达时,只需要简单的把它缓存为持久状态,一旦另一个来自其他源的数据来临时候,只需要发出连接的记录。(实际上你可能希望对未提交的部分数据采用某种垃圾回收机制,这可能是基于记录存在的时间长短的,但是如果例子中只有很少的记录或者根本没有这种未完成join的数据,这个可能不是需要考虑的问题)。

image.png

图 6:对无界数据执行内部联接。 当观察到来自两个源的匹配元素时,就会产生连接。

外连接已经会导致我们已经讨论过的数据完整性问题:一旦连接的一侧已经到达,你怎么知道另一侧是否会到达? 这是非常困难的,所以必须引入一些超时的概念,这就引入了时间元素。 时间元素本质上是一种窗口形式,我们稍后将更仔细地研究它。

Approximation algorithms近似算法

image.png

图 7:计算无界数据的近似值。 数据通过复杂的算法运行,产生的输出数据看起来或多或少像另一边的期望结果。

第二类方法是近似算法,例如approximate Top-Nstreaming K-means。它们采用无限的输入源并提供输出数据。近似算法的优势在于,按照设计,它们的开销很低,并且专为无界数据而设计。缺点是这些算法本身是有限的,算法本身通常很复杂(这使得很难想象出新的算法),并且它们的近似性质限制了它们的实用性。

值得注意的是:这些算法在其设计中通常确实有一些时间元素(例如,某种内置衰减)。并且由于它们在元素到达时对其进行处理,因此该时间元素通常基于处理时间。这对于在其近似值上提供某种可证明的误差范围的算法尤其重要。如果这些错误范围是基于数据按顺序到达的,那么当为算法提供具有不同事件时间偏差的无序数据时,这些算法基本上是不能工作的。

近似算法本身是一个非常棒的主题,但由于它们本质上是时间无关处理的另一个示例(以算法本身的时间特征为模),因此它们使用起来非常简单,但是和我们现在的主题不一致,不值得进一步关注。

Windowing 窗口

用于无界数据处理的另外两种方法其实都是使用窗口的变体。 在深入探讨它们之间的差异之前,首先应该明确窗口是什么意思。 窗口只是获取数据源(无界或有界)并沿时间边界将其切割成有限块进行处理的概念。 下图显示了三种不同的窗口模式:

image.png

图 8:示例窗口策略。 每个示例针对三个不同的键显示,突出显示对齐窗口(适用于所有数据)和未对齐窗口(适用于数据子集)之间的差异。

  • 固定窗口:固定窗口将时间分割成具有固定大小时间长度的段。通常(如图 8 所示),固定窗口的段在整个数据集上均匀应用,这是对齐窗口的一个示例。在某些情况下,需要对数据的不同子集(例如,每个键)的窗口进行相移,以随着随数据变化更均匀地分布窗口完成负载,这就是是未对齐窗口而不是固定窗口了。
  • 滑动窗口:一种类型的固定窗口,滑动窗口由固定长度和固定周期定义。如果周期小于长度,则窗口重叠。如果周期等于长度,则就是一个固定的窗口。如果周期大于长度,则会有一种奇怪的采样窗口,只要一部分的数据可以被观测到。与固定窗口一样,滑动窗口通常是对齐的,但在某些用例中作为性能优化可能会不对齐。请注意,图 8 中的滑动窗口是按原样绘制的,以提供滑动运动的感觉;实际上,所有五个窗口都是给整个数据集工作的。
  • 会话:动态窗口的一个示例,会话由一系列事件组成,这些窗口由大于某些超时的间隙导致产生了间隔。会话通常通过将一系列时间相关的事件(例如,一次观看的视频序列)组合在一起来分析用户随时间变化的行为。会话很有趣,因为它们的长度不能先验定义;它们取决于所涉及的实际数据。它们也是未对齐窗口的典型示例,因为会话在不同的数据子集(例如,不同的用户)中几乎永远不会相同。

我们讨论的两个时间域——处理时间和事件时间——本质上是我们最关心的两个领域[2]。 窗口化在这两个领域都很有意义,所以我们将详细研究每个领域,看看有何不同。 由于处理时间窗口在现有系统中更为常见,我们将从那里开始。

按处理时间的窗口

image.png

图 9:按处理时间窗口化为固定窗口。 数据根据它们到达管道的顺序收集到窗口中

当按处理时间进行窗口化时,系统本质上会将传入的数据缓冲到一个窗口中,直到经过一定量的处理时间。例如,在五分钟固定窗口的情况下,系统会将数据缓冲五分钟时间,之后它将把这五分钟内观察到的所有数据视为一个窗口,并将它们发送到下游进行处理.

处理时间窗口有几个不错的特性:

  • 实现非常简单,因为您从不担心及时调整数据。您只需在它们到达时将它们缓冲起来,并在窗口关闭时将它们发送到下游。
  • 判断窗口完整性很简单。由于系统完全了解窗口的所有输入是否达到,因此它可以对给定的窗口是否完整做出完美的判断。这意味着在按处理时间进行窗口化时,无需以任何方式处理“迟到”数据。
  • 如果您想在根据收到的消息去推断有关源的信息,处理时间窗口正是您想要的。许多监控场景都属于这一类。想象一下,跟踪每秒发送到全球规模 Web 服务的请求数。计算这些请求的速率是处理时间窗口的完美使用。

抛开优点不谈,处理时间窗口有一个非常大的缺点:如果有问题的数据具有与其关联的事件时间,那么如果处理时间窗口需要反映这些事件实际发生的时间,则这些数据必须按事件时间顺序到达。但是,事件时间排序的数据在许多现实世界的分布式输入源中其实并不常见。

举一个简单的例子,想象一下任何收集使用统计信息供以后处理的移动应用程序。如果给定的移动设备离线一段时间(连接短暂中断、飞越全国时处于飞行模式等),则在该设备再次上线之前不会上传在此期间记录的数据。这意味着数据到达时可能会出现几分钟、几小时、几天、几周或更长时间的事件时间偏差。当按处理时间进行窗口化时,基本上不可能从这样的数据集中得出任何有用的推论。

作为另一个例子,当整个系统健康时,许多分布式输入源似乎提供事件时间排序(或非常接近)的数据。不幸的是,当输入源健康时,事件时间偏差较低的事实并不意味着它会一直保持这种状态。考虑处理在多个大洲收集的数据的全球服务。如果带宽受限的横贯大陆线路上的网络问题(这非常普遍)进一步降低了带宽和/或增加了延迟,那么您的输入数据的一部分可能会突然开始以比以前大得多的偏差到达。如果您按处理时间对数据进行窗口化,则您的窗口不再代表其中实际发生的数据;相反,它们代表事件到达处理管道时的时间窗口,这是旧数据和当前数据的任意组合。

在这两种情况下,我们真正想要的是以一种对事件以到达顺序并且按事件时间对数据进行窗口化。我们真正想要的是事件时间窗口。

事件时间窗口

当需要以反映这些事件真实发生的时间观察数据源时,可以使用事件时间窗口。 这是使用窗口的黄金标准。 遗憾的是,当今使用的大多数数据处理系统都缺乏对它的原生支持(尽管任何具有良好一致性模型的系统,如 Hadoop 或 Spark Streaming,都可以作为构建此类窗口系统的合理基础)。

此图显示了将无界数据源窗口化为一小时固定窗口的示例:

image.png

图 10:按事件时间窗口化为固定窗口。 数据根据它们发生的时间收集到窗口中。 白色箭头标出到达处理时间窗口的示例数据,这些时间窗口不同于它们所属的事件时间窗口

该图中的白实线标出了两个感兴趣的数据。 从图中可以看出,这两类数据都到达处理时间窗口,但是与它们所属的事件时间窗口不匹配。 因此,想按照事件时间进行处理,如果使用处理时间窗口,则计算结果将不正确。显然,使用事件时间窗口才能达到事件时间上的正确性。

另外在处理无限数据的时候,使用事件时间窗口,可以创建动态大小的窗口。例如会话窗口,此种情况下,使用固定窗口会将紧密相关联数据分隔到不同的窗口(之前在 “Unbounded data — batch” section):”部分的会话窗口示例中说明过):

image.png 图 11:按事件时间窗口化到会话窗口。 数据被收集到会话窗口中,根据相应事件发生的时间捕获活动的发生。 白色箭头再次标出将数据放入正确的事件时间位置所需的时间shuffle

当然,强大的语义都是有代价的,事件时间窗口也不例外。事件时间窗口有两个明显的缺点,因为窗口通常必须比窗口本身的实际长度更长(在处理时间上):

  • 缓冲:由于窗口持续时间延长,需要更多的数据缓冲。值得庆幸的是,持久存储通常是大多数数据处理系统所依赖的资源类型中最便宜的(其他主要是 CPU、网络带宽和 RAM)。因此,当使用任何设计良好的数据处理系统具有高度一致的持久状态和合适的内存缓存层时,这个问题通常比人们想象的资源耗费要少得多。此外,许多聚合不需要缓冲整个输入集(例如,总和或平均值),而是可以增量执行,将更小的中间聚合存储在持久状态中。
  • 完整性:鉴于我们通常无法知道何时查看了给定窗口的所有数据,我们如何知道该窗口的结果何时可以实现?事实上,我们其实不知道。对于许多类型的输入,系统可以通过类似 MillWheel 的水印(我将在第 2 部分中详细讨论)之类的东西给出一个相当准确的窗口完成的启发式估计。但是在绝对正确至关重要的情况下(再次考虑金融场景),唯一的选择是为管道提供一种方式来表示何时计算结果,以及这些结果如何随着时间的推移而更正。处理窗口完整性(或缺乏完整性)是一个引人入胜的话题,但最好在具体示例的上下文中进行探讨,我们等下在讨论这个问题。

总结

哇!这篇文章真是很多内容。对于那些已经走到这一步的人:你应该受到赞扬!在这一点上,我们大约完成了我想要涵盖的内容的一半,所以退一步,回顾我到目前为止所涵盖的所有信息,并让事情在进入第 2 部分之前稍微总结一下。所有这两篇文章,第 1 部分很多是无聊的术语阐述;第 2 部分才是真正有趣的地方。

总结下,上文包括:

  • 澄清了术语,特别是将”流”(streaming)的定义限定为仅适用于执行引擎,而使用更加描述到位的术语如无限数据近似/推测结果这样不归属于流的范畴的概念。
  • 评估了精心设计的批处理和流式系统的相对功能,假定流式处理事实上是批处理的严格超集,而像 Lambda 架构这样的概念(认为流式处理不如批处理)注定会随着流式处理的成熟而退役。
  • 提出了流式系统赶超批处理系统所需的两个高阶概念,分别是正确性时间推理工具
  • 分析了事件时间和处理时间之间的重要区别,介绍了在分析数据时这些差异所带来的困难,并提出了方法上的转变从完整性的概念转向简单地适应数据随时间而变化
  • 研究了当今世界针对有限和无限数据,批处理和流式处理引擎常用的主要数据处理方法,一般将无限数据处理方法分为以下几种:时间无关近似解基于处理时间的窗口基于事件时间的窗口

接下来

本文为我在第二部分的具体案例做了铺垫,接下来将包含以下几点:

  • 将 Dataflow 模型中的数据处理概念分解为四块:what、where、when、how
  • 详细介绍在多个场景下处理简单、具体的数据集实例,重点介绍 Dataflow 模型所支持的多个用例,以及涉及到的具体 API。这些例子将有助于理解本文介绍的事件时间和处理时间的概念,另外将探索一些新概念,如水位线。
  • 对于现有的数据处理系统,比较这两篇文章中涉及到的一些重要特征,从而更容易从中作出选择,并改善其中的不足,我的终极目标是在整个大数据社区对于数据处理系统,尤其是流式系统的改善。

引用:

[2] 如果您在学术文献或基于 SQL 的流媒体系统中闲逛足够多,您还会遇到第三个窗口时域:基于元组的窗口(即,其大小以元素数量计的窗口)。然而,基于元组的窗口本质上是一种处理时间窗口的形式,其中元素在到达系统时被分配单调递增的时间戳。因此,我们不会在这里详细讨论基于元组的窗口化(尽管我们将在第 2 部分中看到它的示例)。

Streaming 101: The world beyond batch www.oreilly.com/radar/the-w…