今天介绍一篇文章,详细阐述了Apache Spark 2.0突出的三大优点:更容易、更快速、更智能。
两个月前,我们在Databricks上发布了Apache Spark 2.0的预览版本。从下面的图表可以看出,我们的10%的集群已经使用这个版本,同时客户试用了其新功能,并向我们提供了反馈意见。
随时间的推移各个不同版本Apache Spark的使用情况
现在,让我们一起更深入地了解Apache Spark 2.0的新功能。
更容易:ANSI SQL和简化版的API
我们为Spark感到特别自豪的一件事是简单、直观和表达性强的API。Spark 2.0继续了这一传统,其重点关注以下两大领域:
- 标准SQL支持
- 统一DataFrame/Dataset API。
在SQL方面,我们已经大大扩展了Spark的SQL支持功能,并引入了一个新的ANSI SQL解析器和对子查询(subqueries)的支持。现在,Spark 2.0已经可以运行TPC-DS的所有99个查询,这99个查询需要诸多的SQL:2003特性。因为SQL一直是Spark的主要接口之一,因此这些扩展功能大大降低了移植旧应用程序的工作量。
在程序化API方面,我们已经简化了Spark的API:
统一Scala和Java中DataFrames和Datasets:从Spark 2.0开始,DataFrame只是Dataset of Row的类型别名。Dataset类同时支持类型化方法(例如map、filter,groupByKey)和非类型化方法(例如select、groupBy)。此外,这个新的组合型Dataset接口是用于结构化流(Structured Streaming)的抽象。
由于编译时类型安全性在Python和R中并不是语言特性,因此Dataset的概念不适用于这些语言API。相反,DataFrame仍然是它们的主接口,并且类似于这些语言中的单节点数据帧概念。
SparkSession: 这是一个新的切入点,取代了旧的SQLContext和HiveContext。对于DataFrame API的用户而言,Spark的一个常见的容易产生困惑的地方就是我们正在使用哪个“运行环境”。现在您可以使用SparkSession作为单一切入点,其同时涵括了SQLContext和HiveContext的功能。请注意,旧版本的SQLContext和HiveContext类仍然保持向后兼容性。
更容易、更高性能的累加器API(Accumulator API):我们设计了一个新的累加器API(Accumulator API),它具有更加简洁的类型层次结构,并支持原语类型的专门化。旧版本的Accumulator API已经弃用,但仍然保留了向后兼容性。
- 基于DataFrame的机器学习API成为主要的ML API:在Spark 2.0中,spark.ml包及其“管道”API将成为主要的机器学习API。虽然原始spark.mllib包被保留,但未来的开发将集中在基于DataFrame的API上。
- 机器学习管道持久化:用户现在可以利用Spark支持的所有编程语言保存和加载机器学习管道和模型。
- 以R语言编写的分布式算法:增加对以R语言编写的广义线性模型(Generalized Linear Models,GLM)、朴素贝叶斯(Naive Bayes)、生存回归(Survival Regression)和K均值(K-Means)的支持。
以R语言编写的用户定义函数(UDF):增加对运行分区级别UDF(dapply和gapply)和超参数调整(lapply)的支持。
更快速:Apache Spark作为编译器
根据我们2015年Spark调查报告显示,91%的用户认为性能是Apache Spark最为重要的考虑因素。因此,性能优化始终是我们Spark开发的重点。在我们开始规划对Spark 2.0的性能改进之前,我们问了自己一个问题:虽然Spark已经相当快了,但是我们是否可以将其推向性能极限,使Spark 的运行速度再提升十倍呢?
带着这个问题我们从根本上重新思考了Spark物理执行层的设计方式。当您随便调查一个现代数据引擎(例如,Spark或其他MPP数据库)时,您会发现大多数CPU周期都消耗在无用的工作上,比如进行虚拟函数调用或读取/写入中间数据到CPU高速缓存或内存中。通过减少在这些无用工作中浪费的CPU周期数量来优化性能已经是现代编译器的一直以来关注的热点。
Spark 2.0中配备了第二代Tungsten引擎。这一代引擎是建立在现代编译器和MPP数据库的想法上,并且把它们应用于数据的处理过程中。其主要想法是通过在运行期间优化那些拖慢整个查询的代码到一个单独的函数中,消除虚拟函数的调用以及利用CPU寄存器来存放那些中间数据。我们把这些技术统称为“整段代码生成”(whole-stage code generation)。
为了有个直观的感受,我们记录下在Spark 1.6和Spark 2.0中在一个核上处理一行的操作时间(单位是纳秒)。下面的表格能够体现出Spark 2.0中新的Tungsten引擎的威力。Spark 1.6使用的表达式代码生成技术同样在今天的一些最先进的商业数据库中采用,但是您可以看到,许多运算符在采用了“整段代码生成”(whole-stage code generation)技术之后速度提升了一个数量级。
在本笔记本中,您可以看到“整段代码生成”(whole-stage code generation)技术的威力,在这里我们在一台机器上对10亿条记录进行Aggregation和Join操作。
那么在新的Tungsten引擎在端至端的查询表现又会怎样?我们比较了Spark 1.6和Spark 2.0在使用TPC-DS查询的基本分析,如下图所示:
除了“整段代码生成”(whole-stage code generation)可以提高性能之外,Catalyst优化器方面也做了许多的工作,比如改进通用查询优化(例如,为空性传播(nullability propagation));还有一个新的矢量化Parquet解码器,它使得Parquet的扫描吞吐量提高了3倍。
更智能:结构化流(Structured Streaming)
Spark Streaming在大数据领域第一次尝试将批处理和流计算进行了统一。在Spark 0.7版本开始引入的第一个流式API称为DStreams,它为开发者提供了几项强大的特性:恰好一次的语义、大规模容错、强一致性保证和高吞吐量。
然而,随着数百个真实的Spark Streaming部署之后,我们发现,需要实时作出决策的应用程序通常需要不止一个流引擎。他们需要深度地将批处理堆栈和流处理堆栈进行整合;需要和外部存储系统进行交互;以及需要应付业务逻辑变化的能力。其结果是,企业需要的不仅仅是一个流式引擎;相反,他们需要一个完整的堆栈,使他们能够开发终端到终端的“持续应用程序”。
Spark 2.0通过一种称为结构化流(Structured Streaming)的新API来处理这些使用案例。与现有的流系统相比,结构化流(Structured Streaming)主要做了以下三个方面的改进:
1、集成API与批处理作业。如需运行流计算,开发人员只需对DataFrame/Dataset API编写一个批处理计算,然后Spark会自动递增计算以便以流方式运行(即在数据进入时更新结果)。这种强大的设计意味着开发人员不必手动管理状态、故障或保持应用程序与批处理作业同步。相反,流式作业总能给出与同一数据上的批处理作业相同的答案。
2、与存储系统之间的事务交互。结构化流(Structured Streaming)能够在整个引擎和存储系统中保持容错性和一致性,从而可以很容易地编写应用程序,该应用程序能够更新用于服务的实时数据库,加入静态数据或在存储系统之间可靠地移动数据。
3、与Spark其余部分的丰富集成。结构化流(Structured Streaming)支持通过Spark SQL对流数据进行交互式查询,对静态数据进行连接,以及许多已经使用DataFrames的库,同时让开发人员构建完整的应用程序,而不仅仅是流管道。未来,期望与MLlib和其他库实现更多的集成。
作为实现这一愿景的第一步,Spark 2.0附带了一个初始的alpha版本的结构化流(Structured Streaming),其扩展自DataFrame/Dataset API(令人惊讶的小!)。这个统一对现有的Spark用户比较容易适应,因为这让他们能够充分利用Spark批量处理API知识来解决实时中的新问题。其它主要功能将包括支持基于事件时间的处理、乱序/延时数据、交互式查询以及非流数据源和接收器的紧密集成。
此外,我们还更新了Databricks工作空间以支持结构化流(Structured Streaming)。例如,当启动流查询时,笔记本UI将自动显示其状态。
Streaming显然是一个非常宽泛的话题,所以敬请多关注,以了解Apache Spark 2.0中的结构化流(Structured Streaming)的更多详细信息。
本文作者:佚名
来源:51CTO