The world beyond batch: Streaming 102(上篇)
Posted 雨钓Moowei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了The world beyond batch: Streaming 102(上篇)相关的知识,希望对你有一定的参考价值。
The world beyond batch: Streaming 102(上篇)
By Tyler AkidauAugust 5, 2015
雨钓 译 (有增改)
绪论
编者注:这是关于数据处理演变的两部分系列文章的第二篇,重点关注流式系统、无界数据集和大数据的未来。详细的可以查看上一篇文章以及相关书籍,链接如下;See part one. Also, check out "Streaming Systems," by Tyler Akidau, Slava Chernyak, and Reuven Lax.
一、Introduction
欢迎回来如果你错过了之前的文章请访问:streaming-101查看,我强烈建议你花点时间读一遍。 它为我将要在这篇文章中介绍的主题奠定了必要的基础,我假设您已经熟悉了这里介绍的术语和概念。
另外,请注意,这篇文章包含了一些动画,所以那些尝试打印它的人会错过一些最好的部分。
简单回顾一下,上节课我主要讲了三个方面:
-
Terminology:当我使用像“Streaming”这样的有歧义的术语时,重新定义了Streaming的含义,以便于准确地表达。
-
Batch Versus Streaming:比较这两种系统理论上的能力,只要确保两件事,就可以使得流式系统超越批处理系统: 1):correctness ,2):tools for reasoning about time
-
Data Processing Patterns:讨论了在处理有界和无界数据时批处理和流系统所采用的基本方法。
在这篇文章中,我想在上次的数据处理模式部分进一步深入,但会更详细地介绍具体示例。这篇文章的将跨越两个主要部分:
-
Streaming 101 Redux: 简单回顾一下Streaming 101中引入的概念,并添加一个运行示例来突出重点。
-
Streaming 102: Streaming 101的附加部分,详细介绍了在处理无界数据时其他重要的概念,并使用具体示例来解释这些概念。
至此我们已经讨论了我认为的健壮的无序数据处理的核心原则和概念集。通过这些推断时间的工具(tools for reasoning about time)可以让你的流式系统真正超越传统的批处理。
为了让你了解它们的实际情况,我将使用Dataflow SDK代码片段(例如Google Cloud Dataflow的API),与动画相结合,以提供概念的可视化表示.。我之所以使用Dataflow SDK,而不是人们更熟悉的东西,比如SparkStreaming或Storm,是因为目前还没有其他系统能够提供我想要介绍的所有示例中所必需的语义。 好消息是,其他项目正开始朝着这个方向发展, 更好的消息是,我们(谷歌)今天向Apache软件基金会提交了一份创建 Apache Dataflow孵化器项目的提案。 希望围绕Dataflow模型提供的健壮的无序处理语义构建一个开放的社区和生态系统。 这将是一个非常有趣的2016年
这篇文章缺少了我上次承诺的关于流式处理和批处理的对比部分;很抱歉,我错误地低估了我想在这篇文章中加入的内容,以及添加这些内容所需要花费的时间。我在2015年题为The evolution of massive-scale data processing(海量数据处理演化)的演讲以及之后的在16年更新版本中涵盖了我想在这个文章中讨论的内容。幻灯片在这里:available here虽然与本文不完全一样,但仍是有意义的。
现在进入正题:
一、Recap and roadmap
在“Streaming 101”中,我首先阐明了一些概念。首先我通过比较有界数据和无界数据的区别来展开文章。有界数据源有固定的大小,并且通常被称为“batch”数据。无界数据源大小是无界的,经常被称为“Streaming”数据。我尽量避免使用batch 和Streaming来指代数据源, 因为这些名字带有某些含义,这些含义具有误导性,而且往往是受限的。
之后我尝试明确batch和Streaming引擎之间的区别:batch(批处理)引擎是那些在设计时只考虑有界数据的引擎; 而Streaming(流)引擎的设计时则考虑到了无限数据。 我的目标是在提到执行引擎时只使用batch和Streaming两个术语。
我介绍了与无界数据处理相关的两个重要的基本概念, 我首先介绍了event time(事件发生的时间)和processing time(事件被处理pipeline发现的时间)之间的关键的区别。这为Streaming 101的主要结论奠定了基础: 如果你既关心正确性又关心事件发生的背景,你必须通过数据固有的event time进行分析,而不是通过processing time,因为processing time表示的是数据进入pipeline被处理时的时间。
然后我介绍了windowing(沿着时间边界划分数据集) 这是一种在处理无界数据源时常用的技术方法,即使数据源永远不会结束。 窗口策略的一些简单示例是固定窗口和滑动窗口,但是更复杂的窗口类型,比如Sessions也广泛使用( 例如在某些地方,窗口是由数据本身的特性定义的,例如,捕获每个用户的活动会话)
除了这两个概念之外,我们现在还将仔细研究另外三个概念:
-
Watermarks:watermark 是关于事件时间的输入完整性的概念。一个时间为X的watermark具有如下语义: 所有的event time小于X的输入数据都已经被pipeline观察到了。 因此,在观察一个没有已知边界的无界数据源时,watermark作为一种进度度量。
-
Triggers: triggers是一种机制,用于声明窗口的输出何时物化,triggers为何时进行输出提供了灵活性。 这反过来又打开了一扇门,可以随着时间的推移而细化结果,从而允许在数据到达时提供推测结果,以及随着时间的推移而处理上游数据的变化或处理相对wartermark较晚到达的数据(例如, 在移动场景中,一个人的手机记录下他离线时的各种动作和事件时间,然后在恢复连接后上传这些事件进行处理)
-
Accumulation: accumulation模式指的是在同一窗口中观察到的多个结果之间的关系。 这些结果可能完全独立, 例如随时间变化的独立增量,或者它们之间可能有重叠。 不同的accumulation模式具有不同的语义和相应的成本,因此对于不同的模式,都可以在各种用例中找到其适合的应用场景。
最后,为了使得理解所有这些概念之间的关系更容易, 我们将回答下面四个问题,并重新讨论旧的问题以及探索新的问题,我认为所有这些问题对每个无界数据处理都是至关重要的:
-
What results are calculated? 这个问题需要根据pipeline的转换类型(即计算的具体任务的类型)来回答, 例如计算Sum、建立直方图、训练机器学习模型等,这也是经典的批处理问题。
-
Where in event time are results calculated? 要回答这个问题需要在pipeline中使用event time窗口,即pipeline中的窗口是按event time划分的,而不是按照processing time划分。例如 Streaming 101文章中介绍的窗口划分常用示例( fixed, sliding, and sessions),以及没有分窗概念的实例( Streaming 101中描述的时间无关( time-agnostic)处理;经典的批处理通常也属于这一类),以及其他的更多复杂类型的窗口,例如 time-limited auctions。 还要注意,如果将输入时间指定为记录到达系统时的事件时间,它还可以包括 processing-time 窗口 。
-
When in processing time are results materialized? 这个问题的答案是使用watermark和triggers. 这个主题有无数种变体,但最常见的模式是使用watermark来描述给定窗口的输入何时完成,使用triggers可以指定早期结果( 这些结果是推测的,且是在窗口完成之前发出的部分结果,不是最终的)和后期的结果( watermark仅是对完整性的情况进行评估,对于一个窗口而言,即使watermark声明该窗口的输入已经完成,但很可能之后仍会有对应于该窗口的数据进入)。
-
How do refinements of results relate? 这个问题可以使用 accumulation的类型来回答,包括: discarding (当结果都是独立的和互不相关的) accumulating(后面的结果是依赖于前面的结果) accumulating and retracting(累加的值和先前的triggered的值都会被放出)
在接下来的文章中,我们将更详细地讨论这些问题。 是的,我将把这个配色方案应用到实践中,试图让它非常清楚哪些概念与哪些问题相关,比如 What/Where/When/How习语。
二、Streaming 101 redux
首先,让我们回顾一下流式101中介绍的一些概念, 但这一次与一些详细的例子一起,将有助于使这些概念更加具体。
2.1、What: transformations
在经典批处理中 transformations的应用回答了这个问题: “What results are calculated?” 尽管很多人可能已经熟悉了传统的批处理, 无论如何我们都要从批处理开始,因为它是基础,在它之上我们会加入其他所有的概念。
在本节中,我们将查看一个示例: 在由10个值组成的简单数据集中计算整数和。 如果你想要更务实一点的话, 你可以把它看作是计算一队人员玩某种手机游戏的总体得分,通过将队中每个成员的独立的得分组合在一起。 可以想象它同样适用于账单和监控的用例。
对于每个示例,我将包含一个Dataflow Java SDK伪代码片段,以使管道的定义更加具体。 它将是伪代码,因为我有时会修改规则,使示例更清晰,省略细节( 比如使用具体的I/O源) 或简化名称( Java中当前的触发器名称冗长得令人痛苦;我将使用更简单的名称来明确)。 除了诸如此类的小事( 我在附言中明确列举了其中的大部分内容) 它基本上是真实世界的 Dataflow SDK代码。 稍后,我还将为那些对示例感兴趣的人提供一个实际代码演练的链接,他们可以自己编译和运行这些示例。
如果您熟悉Sparkstreaming或Flink之类的内容,那么您应该很容易理解Dataflow代码在做什么。为了给你一堂速成课,在Dataflow中有两个基本元素:
-
PCollections, 代表数据集( 可能是巨大的) 通过它,并行转换( parallel transformations)得以执行( 因此,名称开头有字母“P”)。
-
PTransforms, 使用一个 PCollections 创造一个新的 PCollections , PTransforms可以执行 element-wise转换,他们可以聚合多个元素( aggregate),或者他们是其他的 PTransforms组合( Composite )。
Figure 1. Types of transformations. Image credit: Frances Perry, used with permission.
如果您发现自己很困惑,或者只是想要一个参考资料来参考,那么您可以查看一下 Dataflow Java SDK docs文档。对于我们的例子,我们假设我们从
PCollection<KV<String, Integer>>
开始,命名为“input”(它是一个由String和Integer键值对组成的 PCollection,其中String是队伍的名字,并且integer是队伍中每个独立成员的分数)。在现实的pipeline中,我们可以通过读一个 PCollection来从IO源获取一行数据(例如log记录)然后通过将日志记录转换成适当的键值对把他转化成
PCollection<KV<String, Integer>>
为了清晰起见,在第一个示例中,我将包含所有这些步骤的伪代码,但是在后面的示例中,我省略了I/O和解析部分。
因此,对于一个简单地从I/O源读入数据、解析team/score键值对。并计算每个团队得分总和的pipeline,我们将得到这样的结果:
PCollection<String> raw = IO.read(...);
PCollection<KV<String, Integer>> input = raw.apply(ParDo.of(new ParseFn());
PCollection<KV<String, Integer>> scores = input
.apply(Sum.integersPerKey());
Listing 1. Summation pipeline.
key/value的数据是从I/O数据源读取,String作为key(例如队伍名称), Integer作为值(例如每个成员的分数)然后将每个键的值相加生成每个键的和(例如整个队伍的分数)作为输出的集合
在下面的所有示例中,在看到描述pipeline的代码片段之后,我们将进行分析, 然后,我们将以动画的方式展示在具体数据集上pipeline代码是如何执行的。 更具体地说,我们将看到为一个键对应超过10个输入数据的pipeline在执行时是什么样子。 在实际的pipeline中,您可以想象类似的操作将在多台机器上并行进行,但是为了我们的示例,让事情保持简单,以便会更清晰。
每个动画在两个维度上绘制输入和输出:X轴为event time,Y轴为processing time。因此,pipeline从底部到顶部的实时观测由上升的白色线表示。 输入用圆圈表示,圆圈内的数字表示特定记录的值, 它们从灰色开始,随着pipeline的观察而改变颜色.
当pipeline观察输入的数据时, pipeline积累数据的状态,并最终将聚合结果作为输出。 状态和输出用矩形表示,聚合的结果在顶部, 矩形覆盖的区域表示eventtime和processing time的部分已经算入到累计结果中。对于listing 1 中对应代码的pipeline, 在一个经典的批处理引擎上执行时会是这样的
Figure 02 - classic batch(点击链接查看动画)
因为这是一个批处理管道,所以它会累积状态,直到看到所有输入( 由顶部绿线虚线表示)当看到所有输入时,它的单个输出为51。 在本例中,我们计算所有event time数据的总和,因为我们没有应用任何特定的窗口转换; 因此,状态和输出的矩形覆盖了X轴的全部。 如果我们想处理无界数据源,此时,传统的批处理是无法实现的:我们不能等待输入结束,因为它实际上永远不会结束。 我们需要一个概念,即窗口( windowing),我们在Streaming 101中介绍过。此时对应于第二个问题: “Where in event-time are results calculated?”,我们现在简要回顾一下窗口的概念:
2.2、Where: windowing
正如上次讨论的,窗口是沿着时间边界分割数据源的过程。 常见的窗口策略包括固定窗口( fixed windows)、滑动窗口( sliding windows)和会话窗口( sessions windows):
Figure 3. Example windowing strategies.
每个示例显示三个不同的键,突出显示对齐窗口和非对齐窗口之间的差异。
为了更好地理解实际的窗口是什么样子,让我们将整数求的pipeline中窗口设置为固定的,且固定窗口大小为2分钟。 对于Dataflow SDK,更改是对 Window.into转换的简单添加。
PCollection<KV<String, Integer>> scores = input
.apply(Window.into(FixedWindows.of(Duration.standardMinutes(2))))
.apply(Sum.integersPerKey());
Listing 2. Windowed summation code.
回想一下,Dataflow提供了一个统一的模型,可以在批处理和流处理中工作,因为批处理语义实际上只是流处理的一个子集。 因此,让我们首先在批处理引擎上执行这个管道;当我们切换到流引擎时,这会给我们一些可以比较的东西。
figure-4 (点击链接查看动画)
和以前一样,输入的数据保持其原有状态进行积累,直到完全消费完,然后再产生输出。 然而,在这种情况下,我们得到的不是一个输出,而是四个:针对4个长度为两分钟的event time窗口,为每个窗口提供一个输出。
至此,我们重新讨论了Streaming 101中引入的两个主要概念:event time和processing time 两个时间域的关系,以及windowing.。如果我们想更进一步,我们需要开始添加在本节开头提到的新概念: watermarks, triggers, and accumulation。至此我们开始Streaming 102。
三、Streaming 102
我们刚刚观察了批处理引擎上的开窗pipline的执行情况.。但是,理想情况下,我们希望结果有较低的延迟,而且我们还希望自然的处理无界数据源。 切换到流引擎是朝着正确方向迈出的一步,但是批处理引擎有一个已知的时间点,即在这个时间点上每个窗口的输入都完成了( 有界输入源中的所有数据都已被消费,即进入pipeline)。目前,我们缺乏一种行之有效的方法来确定无界数据源的完整性。此时就需要引入watermark的概念。
3.1、When: watermarks
watermark是对该问题前半部分的回答:“When in processing time are results materialized?” watermark是event-time时域中关于输入完整性的时间概念。 换句话说,它们是系统对事件流中正在处理的记录的event time进度和完整性的度量方式( 有界的或无界的,虽然它们在无界的情况下更明显有用)。
回想一下Streaming 101中的这个图, 这里稍作修改,我将event time和processing time之间的偏差描述为大多数实际分布式数据处理系统随时间不断变化的函数。
Figure 5. Event time progress, skew, and watermarks. Credit: Tyler Akidau.
我所说的那条代表现实的弯弯曲曲的红线本质上就是watermark; 它以processing time来衡量event time的完整性。 从概念上讲,你可以把watermark想象成一个函数,
即输入一个processing time中的一个点,返回event time中的一个对应点。( 更准确地说,函数的输入实际上是pipeline中watermark所在点上游的所有事物的当前状态,例如: 输入源、缓冲数据、正在处理的数据等。 但从概念上讲,把它看作是从processing time到event time的映射更简单。) event time点E是系统认为所有event time小于E的输入都被pipeline观测到的点。 换句话说,这是一个断言,event time小于E的数据不会再出现(因为小于E的数据都已经进入pipeline,即使之后pipeline收到迟到的数据,那这些数据对应的event time也肯定都大于E)。 watermark的类型分为:完美或启发式( perfect or heuristic),这个断言可能是严格的保证或一个有根据的猜测,具体如下:
-
Perfect watermarks: 如果我们对所有的输入数据都有很好的了解, 那么构建一个perfect watermark是可能的。 在这种情况下,不存在所谓的迟到的数据;所有数据都是提前或准时的进入pipeline的。
-
Heuristic watermarks: 对于许多分布式输入源来说,完全了解输入数据是不切实际的, 在这种情况下,最好的选择是提供一个Heuristic watermark。Heuristic watermark 使用关于输入的任何可用信息(分区、分区内的排序(如果有的话)、文件的增长率等)来提供尽可能准确的进度估计。 在许多情况下,这样的watermark可以非常准确地预测。 即使如此,使用Heuristic watermark意味着它有时可能是错误的,即将存在延迟数据。 我们将在下面的 triggers 部分了解处理延迟数据的方法
watermark是一个令人着迷和复杂的话题,有太多的东西要讨论,我不能在这里一一说明,所以对它们的进一步深入研究将不得不放在之后的文章中。 现在,为了更好地了解watermark所扮演的角色以及它们的一些缺点,让我们来看两个例子,一个流引擎单独使用watermark来确定在执行list 2中的窗口管道时何时物化输出。 左边的例子使用了完美的水印;右边的那个用的是启发式水印。
在这两种情况下,当watermark通过窗口的末端时,窗口被物化了。 两种执行方式的主要区别在于,右侧watermark计算中使用的Heuristic watermark没有考虑到值为9的输入,因此极大的改变了watermark的形状。 这些例子突出了watermark的两个缺点( 以及其他完整性的概念)具体来说是:
-
Too slow: (对于Perfect watermarks而言的)如果确定存在延迟到达的数据( 例如,由于网络带宽限制,输入日志增长缓慢),而由于此你的watermark被延迟了,此时可能会导致你窗口输出出现延迟。 这一点在左边的图中最为明显,迟到的9延后了所有后续窗口的watermark,即使这些窗口的输入数据更早完成,也必须等待9到达。这对于第二个窗口(即12:02-12:04)尤为明显,对于这个窗口而言,从窗口中的第一个值出现到我们看到窗口的结果几乎需要7分钟 。 本例中的Heuristic watermark没有遇到这样的问题, 但这并不意味着启Heuristic watermark永远不会出现watermark延迟。 这里的重点是:虽然watermark提供了一个非常有用的完整性概念, 从延迟的角度来看,依赖于生成输出的完整性通常并不理想。 想象一个包含有价值指标的仪表盘,且按小时或天划分窗口。 您不太可能想要等待一个小时或一天结束才能查看当前窗口的结果, 这是使用经典批处理系统在处理此类问题是遇到的痛点之一。相反, 如果窗口的结果可以随着时间的推移以及数据的不断输入而不断改进甚至是完善,那么将更加实用。
-
Too fast: (对于Heuristic watermarks而言的)当Heuristic watermark相较于实际情况发生了错误的提前时,同时event-time在watermark之前的数据延迟一段时间到达pipeline也是有可能的,两个情况同时出现时就会出现延迟数据。 右边的例子就是这样, 在第一个窗口的所有输入数据还没有被观察到之前,watermark已经前进到第一个窗口的末尾,导致第一个窗口输出错误的值5,而不是14。 这一缺陷是Heuristic watermark的一个严重问题;它们的启发式本质意味着它们有时是错误的。 因此,如果您关心正确性,仅依靠它们来确定何时物化输出是不够的。
在Streaming 101中,我强调了一些关于完整性概念的声明,完整性的概念对于无界数据流无序的处理的健壮性而言是不够的。 这两个缺点,watermark太慢或太快,就是这些论点的基础。 对于一个完全依赖完整性概念的系统,您无法同时获得低延迟和正确性。而triggers就是用来解决这些缺点的。
3.2、When: The wonderful thing about triggers, is triggers are wonderful things!
triggers可以答案问题的后半部分: “When in processing time are results materialized?” trigger声明窗口的输出应该何时输出( 尽管触发器本身可能根据发生在其他时间域的事情做出这些决定, 例如watermark在event-time内的进展) 窗口的每个特定输出被称为窗口的 pane(窗格)。
用于触发trigger的信号包括:
-
Watermark progress (i.e., event time progress), 我们已经在图6中看到了一个含蓄的版本,当watermark经过窗口末端时输出才被物化。 另一个用例是当窗口的时间超过某个有用的范围时触发垃圾收集,稍后我们将看到这个例子。
-
Processing time progress, 由于processing time与event time不同,processing time进展或多或少是稳定的,而且没有延迟。所以提供定时的周期性的数据更新是有用的
-
Element counts, 在一个窗口中观察的元素的个数对于trigger非常用。
Punctuations, 或其他依赖于数据的触发器, 记录的某些记录或特性(例如EOF元素或刷新事件)表明应该生成输出。
除了基于具体信号触发的简单trigger之外,还有 composite trigger,允许创建更复杂的触发逻辑, composite triggers例子包括:
-
Repetitions, 与processing time一起使用时特别有用,尤其是当提供定期的周期性的更新时。
-
Conjunctions (logical AND), 只有当所有子trigger都已触发时才会触发(例如,watermark经过窗口的末尾后,我们观察到终止点的记录标志)。
-
Disjunctions (logical OR), 在任何子trigger触发后触发(例如,在watermark通过窗口的末端或我们观察到终止的记录标志之后)。
-
Sequences, 按预先定义的顺序触发一系列子trigger。
为了使触发器的概念更加具体(并提供一些可构建的东西),让我们将图6中使用的隐式默认触发器添加到list 2中的代码中,使其显式:
PCollection<KV<String, Integer>> scores = input
.apply(Window.into(FixedWindows.of(Duration.standardMinutes(2)))
.triggering(AtWatermark()))
.apply(Sum.integersPerKey());
Listing 3. Explicit default trigger.
考虑到这一点,以及完成了对trigger的基本理解之后,我们可以考虑解决watermark太慢或太快的问题。 在这两种情况下,我们基本上都希望为给定的窗口提供某种常规的、物化的更新, 无论是在watermark经过窗口末尾之前还是之后。所以需要一些重复触发,那么问题就变成了我们在重复什么?
针对于too slow的情况(提供较早的推测性的结果) 我们可能应该假设对于任何给定的窗口都有稳定数量的传入数据,因为我们知道( 定义为处于窗口的早期阶段)到目前为止,我们观察到的窗口输入是不完整的。 因此,随着processing time的推进周期性(例如每隔一分钟)的触发trigger, 这可能是明智的,因为trigger触发的次数并不取决于窗口实际观察到的数据量;在最坏的情况下,我们只会得到一个稳定的周期性触发流。
对于too fast的情况( 提供更新的结果,以响应由于 heuristic watermark延迟的数据), 让我们假设我们的watermark是基于一个相对准确的heuristic( 通常是一个相当安全的假设)。 在这种情况下,我们不希望经常看到迟到的数据,但当我们看到时,最好能迅速修改结果。 在观察到迟到数据后,元素计数为1,之后执行trigger,将会给我们的结果提供快速更新( 只要我们看到最新数据,就会立即采取行动), 但鉴于预期的迟到的数据的频率较低,该系统不太可能被压垮。
请注意,这些只是示例: 如果适用于当前的用例,我们可以自由选择不同的trigger。
最后,我们需要编排这些不同trigger的时间; early, on-time, and late(即处理早到,准点,迟到的数据).我们可以通过使用一个 Sequence trigger或者特殊的 OrFinally trigger来完成这些, 他会设置子trigger,在子trigger触发时终止父trigger。
PCollection<KV<String, Integer>> scores = input
.apply(Window.into(FixedWindows.of(Duration.standardMinutes(2)))
.triggering(Sequence(
Repeat(AtPeriod(Duration.standardMinutes(1)))
.OrFinally(AtWatermark()),
Repeat(AtCount(1))))
.apply(Sum.integersPerKey());
Listing 4. Manually specified early and late firings.
不过,这有点啰嗦。考虑到| repeated-early | on-time | repeated-late 的现象非常普遍,我们提供了一个Dataflow API机制 使指定这样的触发器更简单、更清楚。
PCollection<KV<String, Integer>> scores = input
.apply(Window.into(FixedWindows.of(Duration.standardMinutes(2)))
.triggering(
AtWatermark()
.withEarlyFirings(AtPeriod(Duration.standardMinutes(1)))
.withLateFirings(AtCount(1))))
.apply(Sum.integersPerKey());
Listing 5. Early and late firings via the early/late API.
在流式引擎上执行listing 4、Listing5(与前面一样,带有完美的启发式水印),结果如下:
这个版本比图6有两个明显的改进:
对于第二个窗口[12:02,12:04]的watermark too slow的情况:我们提供一个周期的早期更新。 在 perfect 的水印案例中,差异最为明显。 第一次输出的时间从7分钟减少到3.5分钟; 但它在heuristic的情况下也得到了明显的改进。 这两个版本现在都随着时间的推移提供了稳定的改进(窗格的值分别为7、14和22) ,这使得从输入完成到窗口中窗格最终物化输出之间的延迟相对最小
对于第一个窗口【12:00,12:02】heuristic watermark的 to fast 的情况; 当值9出现较晚时,我们立即将其合并到一个新的、经过更正的窗格中,值为14。
这些新触发器的一个有趣的副作用是,它们有效地规范化了perfect watermark 和heuristic watermark版本之间的输出模式。 虽然图6中的两个版本完全不同,但是这里的两个版本看起来非常相似。
此时,剩下的最大差异是窗口生命周期界限。 在perfect watermark情况下,我们知道,一旦watermark通过了窗口的末尾,我们将再也看不到任何数据,因此我们可以在那个时候删除窗口的所有状态。在heuristic watermark的情况下 我们仍然需要保持窗口状态一段时间来等待延迟数据。 但是到目前为止,我们的系统还不能很好地知道每个窗口需要保持多长时间的状态。 这是 allowed lateness(允许迟到)所要介绍的。
内容太多,后续内容转下篇<<The world beyond batch: Streaming 102(下篇)>> 未完待续。。。。。。。。。。。
版权声明:本文为博主原创文章,首发公众号,blog同步更新,欢迎转载,转载请注明出处。https://blog.csdn.net/u012802702/article/details/86441815 公众号地址:
以上是关于The world beyond batch: Streaming 102(上篇)的主要内容,如果未能解决你的问题,请参考以下文章
The Beam Model:Stream & Tables翻译(上)