Spark权威指南(中文版)----第20章 流处理基础

Spark The Definitive Guide(Spark权威指南) 中文版。本书详细介绍了Spark2.x版本的各个模块,目前市面上最好的Spark2.x学习书籍!!!

扫码关注公众号:登峰大数据,阅读中文Spark权威指南(完整版),系统学习Spark大数据框架!

如果您觉得作者翻译的内容有帮助,请分享给更多人。您的分享,是作者翻译的动力

流处理是不断地合并新数据以计算结果的行为。在流处理中,输入数据是无界的,没有预定的开始或结束。它只是形成一系列到达流处理系统的事件(例如,信用卡交易、网站点击或来自物联网(IoT)设备的传感器读数)。然后,用户应用程序可以计算这个事件流上的各种查询(例如,跟踪每种事件类型的运行计数,或者将它们聚合到每小时的窗口中)。应用程序将在运行时输出结果的多个版本,或者可能在外部“接收器”系统(如键值存储)中使其保持最新。

当然,我们可以将流处理与批处理进行比较,批处理中的计算是在固定输入数据集上运行的。通常,这可能是数据仓库中的大型数据集,其中包含来自应用程序的所有历史事件(例如,过去一个月的所有网站访问数据或传感器读数)。批处理也需要一个查询来计算,类似于流处理,但是只计算一次结果。

虽然流处理和批处理听起来不同,但在实践中,它们常常需要一起工作。为了处理这些需求,结构化流(Struct Streaming)从一开始就设计了与Spark的其余部分(包括批处理应用程序)轻松地互操作的功能。实际上,结构化流开发人员创造了术语“连续应用程序”来捕获端到端应用程序,这些应用程序由流、批处理和交互式作业组成,这些作业都在相同的数据上工作,以交付最终产品。结构化流的重点是使以端到端方式构建此类应用程序变得简单,而不是仅仅处理每条记录的流级处理。

20.1.流处理场景

我们将流处理定义为无界数据集的增量处理,但这是激发用例的一种奇怪的方式。在我们讨论流处理的优点和缺点之前,让我们解释一下为什么要使用流处理。我们将描述六个来自底层流处理系统的需求不同的常见用例。

1、通知和预警

最明显的流用例可能是通知和预警。给定一系列事件,如果发生某种事件或一系列事件,应该触发通知或警报。这并不一定意味着自主或预先编程的决策;警报还可以用于将需要采取的某些操作通知对应的人员。一个例子可能是向一个中心的员工发出警报,他们需要从仓库的某个位置获取某个物品并将其发送给客户。在这两种情况下,通知都需要快速进行。

2、实时报表

许多公司使用流处理系统来运行任何员工都可以查看的实时仪表板。我们使用这些指示板来监控平台的总体使用情况、系统负载、正常运行时间,甚至在新特性推出时对它们的使用情况。

3、增量ETL

最常见的流应用程序之一是减少公司在将信息重新存储到数据仓库时必须忍受的延迟。Spark批处理作业通常用于提取、转换和加载(ETL)工作负载,这些工作负载将原始数据转换为Parquet之类的结构化格式,以支持高效查询。使用结构化流,这些作业可以在几秒钟内合并新数据,使用户能够在下游更快地查询数据。在这个用例中,以容错的方式精确地处理一次数据是至关重要的: 我们不希望在输入数据到达仓库之前丢失任何数据,也不希望加载相同的数据两次。此外,流系统需要以事务的方式更新数据仓库,以免将在其上运行的查询与部分写入的数据混淆。

4、实时更新数据

流系统经常用于计算由另一个应用程序交互提供服务的数据。例如,像谷歌analytics这样的web分析产品可能会持续跟踪每个页面的访问次数,并使用流媒体系统来保持这些访问次数的最新。当用户与产品的UI交互时,这个web应用程序查询最新的计数。支持这个用例需要流系统能够以同步的方式对键值存储(或其他服务系统)执行增量更新,而且通常这些更新是事务性的,就像ETL中的情况一样,避免破坏应用程序中的数据。

5、实时决策

流系统上的实时决策包括分析新输入并使用业务逻辑自动响应它们。一个示例用例是,银行希望根据客户的近期历史自动验证客户信用卡上的新交易是否代表欺诈,如果费用是预先确定的欺诈金额阈值,则拒绝该交易。这个决策需要在处理每个事务时实时做出,这样开发人员就可以在流系统中实现这个业务逻辑,并针对事务流运行它。这种类型的应用程序可能需要维护每个用户的大量状态,以跟踪其当前的支出模式,并自动将此状态与每个新事务进行比较。

6、在线机器学习

实时决策用例的一个密切的派生是在线机器学习。在这个场景中,您可能希望在多个用户的流数据和历史数据的组合上训练模型。一个例子可能比前面提到的信用卡交易用例更复杂:与其根据一个客户的行为对硬编码的规则作出反应,公司可能希望不断地从所有客户的行为更新模型,并根据它测试每个交易。对于流处理系统来说,这是最具挑战性的用例,因为它需要跨多个客户的聚合、针对静态数据集的连接、与机器学习库的集成以及低延迟响应时间。

20.2.流处理的优点

现在我们已经看到了流的一些用例,让我们来明确流处理的一些优势。在大多数情况下,批处理对于大多数用例来说更易于理解、排除故障和编写应用程序。此外,批处理数据的能力允许比许多流系统更高的数据处理吞吐量。然而,流处理在两种情况下是必不可少的。首先,流处理可以降低延迟:当您的应用程序需要快速响应(以分钟、秒或毫秒为单位)时,您将需要一个流系统,该系统可以将状态保存在内存中,以获得可接受的性能。我们描述的许多决策和预警用例都属于这个阵营。其次,流处理在更新结果方面也比重复批处理作业更有效,因为它会自动增加计算量。例如,如果我们希望计算过去24小时内的web流量统计数据,那么一个天真实现的批处理作业可能在每次运行时扫描所有数据,总是处理24小时的数据。相反,流媒体系统可以记住以前计算的状态,只计算新数据。例如,如果您告诉流媒体系统每小时更新您的报告,那么它每次只需要处理1小时的数据(自上次报告以来的新数据)。在批处理系统中,您必须手动实现这种增量计算,以获得相同的性能,这将导致流系统自动为您提供大量额外的工作。

20.3.流处理的挑战

我们讨论了流处理的动机和优点,但是您可能知道,天下没有免费的午餐。让我们讨论在流上操作的一些挑战。为了演示这个例子,让我们假设我们的应用程序从传感器(例如汽车内部)接收输入消息,传感器在不同的时间报告它的值。然后我们希望在这个流中搜索特定的值,或特定的值模式。一个具体的挑战是输入记录可能无序地到达我们的应用程序:例如,由于延迟和重新传输,我们可能会按顺序收到以下更新序列,其中time字段显示实际测量值的时间:

{value: 1, time: "2017-04-07T00:00:00"}{value: 2, time: "2017-04-07T01:00:00"}{value: 5, time: "2017-04-07T02:00:00"}{value: 10, time: "2017-04-07T01:30:00"}{value: 7, time: "2017-04-07T03:00:00"}

在任何数据处理系统中,我们都可以构造逻辑来执行一些基于接收单个值“5”的操作。在流媒体系统中,我们还可以快速响应这个单独的事件。然而,如果您只想根据接收到的特定值序列(比如2、10、5)触发某个操作,那么事情就变得更复杂了。在批处理的情况下,这并不特别困难,因为我们可以简单地按时间字段对所有事件进行排序,从而看到10确实在2到5之间。然而,这对于流处理系统来说比较困难,原因是流处理系统将单独接收每个事件,并且需要在事件之间跟踪一些状态,以记住2和5个事件,并认识到10个事件介于两者之间。需要记住流上的这种状态会带来更多的挑战。例如,如果您有大量的数据量(例如,数百万个传感器流),而状态本身是大量的,该怎么办?如果系统中的机器发生故障,丢失一些状态,该怎么办?如果负载不平衡,一台机器运行缓慢怎么办? 当对某些事件的分析“完成”时(例如,2-10-5模式没有发生),您的应用程序如何向下游消费者发出信号? 它应该等待固定的时间还是无限期地记住某个状态? 当您想部署流应用程序时,可能会遇到所有这些挑战和其他挑战,比如生成系统事务的输入和输出。总而言之,我们在前一段和其他几段中所描述的挑战如下:

  • 基于应用程序时间戳(也称为事件时间)处理无序数据

  • 维持大量的状态

  • 支持高数据吞吐量

  • 尽管机器出现故障,但每个事件仅处理一次

  • 处理负载不平衡和掉队

  • 以低延迟响应事件

  • 与其他存储系统中的外部数据连接

  • 确定在新事件到达时如何更新输出接收器

  • 以事务方式将数据写入输出系统

  • 在运行时更新应用程序的业务逻辑

这些主题都是大型流处理系统研究和开发的活跃领域。为了理解不同的流处理系统是如何应对这些挑战的,我们描述了一些常见的设计概念。

20.4.流处理设计中的要点

为了支持我们描述的流处理挑战,包括高吞吐量、低延迟和无序数据,有多种方法可以设计流处理系统。我们在这里描述最常见的设计选项,然后在下一节描述Spark的选择。

20.4.1.每次一条记录 API与声明式API的比较

设计流API最简单的方法是将每个事件传递给应用程序,并让它使用定制代码进行响应。这是许多早期流系统(如Apache Storm)实现的方法,当应用程序需要完全控制数据处理时,它具有重要的地位。提供这种"每次一条记录"API的流只是给用户一个“管道”集合,以便将它们连接到一个应用程序中。然而,这些系统的缺点是,我们前面描述的大多数复杂因素,如维护状态,都是由应用程序单独控制的。例如,对于"每次一条记录"API,用户负责在更长的时间内跟踪状态,在一段时间后删除状态以清空空间,并在失败后以不同的方式响应重复的事件。正确地为这些系统编程是相当具有挑战性的。重要的是,底层api需要具有深入专业知识的人员来开发和维护。

因此,许多较新的流系统提供声明性api,应用程序在其中指定要计算什么,而不是如何计算以响应每个新事件,以及如何从失败中恢复。例如,Spark的原始DStreams API提供了基于流上的map、reduce和filter等操作的功能性API。在内部,DStream API自动跟踪每个操作符处理了多少数据,可靠地保存了任何相关状态,并在需要时从故障中恢复计算。谷歌Dataflow和Apache Kafka Streams等系统提供了类似的功能性api。Spark的结构化流实际上进一步扩展了这一概念,从功能操作切换到关系操作(类似sql),这些操作无需编程即可实现更丰富的自动优化执行。

20.4.2.事件时间与处理时间

对于具有声明性api的系统,第二个问题是系统本身是否支持事件时间。事件时间是基于插入到源记录中的时间戳来处理数据的概念,而不是基于流应用程序接收记录的时间(称为处理时间)。特别是在使用事件时间时,记录可能会无序地到达系统,并且不同的源之间也可能不同步(对于相同的事件时间,一些记录可能比其他记录晚到达)。如果您的应用程序从可能会延迟的远程数据源(如移动电话或物联网设备)收集数据,事件时间处理非常重要:如果没有事件时间处理,当某些数据延迟时,您将错过重要的处理模式。相反,如果应用程序只处理本地事件(例如,在同一数据中心中生成的事件),则可能不需要复杂的事件处理。

在使用事件时间时,多个问题成为应用程序之间的常见问题,包括以一种允许系统合并延迟事件的方式跟踪状态,以及确定在事件时间中为给定时间窗口输出结果(例如:当系统可能已经接收到所有的输入到那一时间点)。正因为如此,许多声明性系统(包括结构化流)的所有api中都集成了对事件时间的“原生”支持,因此可以在整个程序中自动处理这些问题。

20.4.3.连续处理及微批处理

您经常看到的最终设计决策是关于连续执行还是微批处理执行之间的选择。在基于连续处理的系统中,系统中的每个节点都在不断地侦听来自其他节点的消息,并向其子节点输出新的更新。例如,假设您的应用程序在多个输入流上实现了map-reduce计算。在一个连续处理系统中,每个实现map的节点将逐个从输入源读取记录,计算其在这些记录上的函数,并将它们发送到适当的reducer。reducer程序将在获得新记录时更新其状态。关键思想是,这发生在每个单独的记录上,如图20-1所示。

连续处理的优点是,当总输入速率相对较低时,它提供了尽可能低的延迟,因为每个节点都会立即响应一条新消息。然而,连续处理系统通常具有较低的最大吞吐量,因为它们对每条记录都会产生大量的开销(例如,调用操作系统向下游节点发送数据包)。

相比之下,微批处理系统等待积累小批输入数据(比如500毫秒),然后使用分布式任务集合并行处理每个批处理,类似于在Spark中执行批处理作业。微批处理系统通常可以实现每个节点的高吞吐量,因为它们利用了与批处理系统相同的优化(例如,向量化处理),并且不产生任何额外的每个记录开销,如图20-2所示。

因此,它们需要更少的节点来处理相同的数据速率。微批处理系统还可以使用动态负载平衡技术来处理不断变化的工作负载(例如,增加或减少任务数量)。然而,缺点是由于等待累积微批处理而导致更高的基础延迟。在实践中,流应用程序的规模大到需要分发它们的计算,因此倾向于优先考虑吞吐量,因此Spark传统上实现了微批处理。然而,在结构化流中,有一种积极的开发工作也支持同一API下的连续处理模式。

在这两种执行模式之间进行选择时,您应该记住的主要因素是所需的延迟和总操作成本(TCO)。根据应用程序的不同,微批处理系统可以轻松地将延迟从100毫秒延迟到每秒。在这种情况下,它们通常需要更少的节点来实现相同的吞吐量,从而降低操作成本(包括由于更少的节点故障而降低的维护成本)。对于低得多的延迟,您应该考虑使用连续处理系统,或者使用微批处理系统与快速服务层结合来提供低延迟查询(例如,将数据输出到MySQL或Apache Cassandra,在那里可以在毫秒内将数据送达客户机)。

20.5.Spark’s Streaming APIs

我们介绍了一些用于流处理的高级设计方法,但是到目前为止,我们还没有详细讨论Spark的api。Spark包括两个流api,如本章开头所讨论的。Spark流中的早期DStream API是完全面向微批处理的。它有一个声明性的(基于函数的)API,但是不支持事件时间。新的结构化流API添加了更高级别的优化、事件时间和对连续处理的支持。

20.5.1.The DStream API

Spark最初的DStream API自2012年首次发布以来,就被广泛用于流处理。例如,在Datanami 2016年的调查中,DStreams是使用最广泛的处理引擎。由于Spark的高级API接口和简单的精确一次语义,许多公司在生产中大规模使用和操作Spark流。Spark流还支持与RDD代码的交互,比如与静态数据的连接。操作Spark流并不比操作普通Spark集群困难多少。然而,DStreams API有几个限制。首先,它完全基于Java/Python对象和函数,而不是数据框架和数据集中更丰富的结构化表概念。这限制了引擎执行优化的机会。其次,API纯粹基于处理时间来处理事件时间操作,应用程序需要自己实现它们。最后,DStreams只能以微批处理方式进行操作,并在其API的某些部分公开微批处理的持续时间,因此很难支持其他执行模式。

20.5.2.Structured Streaming

结构化流是基于Spark的结构化API构建的高级流API。它适用于所有运行结构化处理的环境,包括Scala、Java、Python、R和SQL。与DStreams一样,它也是一个基于高级操作的声明性API,但是通过在本书前一部分介绍的结构化数据模型的基础上进行构建,结构化流可以自动执行更多类型的优化。然而,与DStreams不同,结构化流具有对事件时间数据的原生支持(其所有窗口操作符都自动支持它)。从Apache Spark 2.2开始,系统只在微批处理模型中运行,但Databricks的Spark团队已经宣布了一项名为持续处理的工作,以添加持续执行模式。这应该成为Spark 2.3中用户的一个选项。

可以使用Apache Spark轻松构建端到端连续应用程序,Apache Spark结合了流、批处理和交互式查询。例如,结构化流不使用独立于DataFrames的API:您只需编写一个普通的DataFrame(或SQL)计算并在流上启动它。当数据到达时,结构化流将以增量方式自动更新此计算的结果。在编写端到端数据应用程序时,这是一个主要的帮助:开发人员不需要维护批处理代码的单独流版本(可能针对不同的执行系统),而且可能会导致这两个版本的代码不同步。另一个例子是,结构化流可以将数据输出到Spark SQL可用的标准接收器,例如Parquet表,从而可以轻松地从另一个Spark应用程序查询流状态。在Apache Spark的未来版本中,我们期望越来越多的项目组件与结构化流集成,包括MLlib中的在线学习算法。一般来说,结构化流是Spark streams的DStream API的一种更易于使用和性能更高的改进,因此在本书中我们将只关注这个新API。许多概念,例如用转换图构建计算,也适用于DStreams,但是我们将对这些概念的阐述留给其他书籍。

20.6.结论

本章介绍了理解流处理所需的基本概念和思想。本章介绍的设计方法应该阐明如何评估给定应用程序的流系统。您还应该能够轻松地理解DStreams和结构化流的作者所做的权衡,以及在使用结构化流时直接支持DataFrame程序的原因:不需要复制应用程序逻辑。在接下来的章节中,我们将深入研究结构化流来理解如何使用它。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章