Spark Release 2.2.0 最新版本发布,Spark 2.2.0是Spark 2.x中第一个在生产环境可以使用的版本,对于Spark具有里程碑意义

第2章 Spark 2.X技术及原理

Apache官方网站于2017711日发布了Spark Release 2.2.0版本, Apache Spark 2.2.0版本是Spark 2.x系列上的第三个版本。Spark 2.2.0Spark 2.x中第一个在生产环境可以使用的版本,对于Spark具有里程碑意义。Spark 2.2.0版本中 Structured Streaming 的实验性标记(experimental tag)已经被移除,此版本更多地侧重于系统的可用性(usability)、稳定性(stability)以及代码的polish,解决了1100tickets。此外,只要安装pyspark,在Spark 2.2.0版本中PySpark可用于pypiSpark 2.2.0版本移除了对 Java 7 以及 Hadoop 2.5及其之前版本的支持,移除了对Python 2.6的支持。

Apache Spark2.2.0版本的一些新功能:

·          Core and Spark SQL

·          Structured Streaming

·          MLlib

·          SparkR

·          GraphX

·          Deprecations

·          Changes of behavior

·          Known Issues

·          Credits

如无特殊说明,本书所有内容都基于最新最稳定的Spark 2.2.0版本的源代码编写,为体现Spark源代码的演进过程,部分核心源代码在Spark 1.6.xSpark 2.1.x源码的基础上,新增Spark 2.2.0版本的源代码,便于读者系统比对、研习Spark源码。

2.1   Spark2.X综述  

Spark 2.0中更新发布了新的流处理框架(Structured Streaming);对于API的更新,Spark 2.0版本API的更新主要包括DataFrame、DataSet、SparkSession、累加器API、Aggregator API等API的变动。

2.1.1 连续应用程序  

自从Spark得到广泛使用以来,其流处理框架Spark Streaming也逐渐地吸引到了很多用户,得益于其易用的高级API和一次性语义,使其成为使用最广泛的流处理框架之一。但是我们不仅仅需要流处理来构建实时应用程序,很多时候我们的应用程序只有一部分需要用到流处理,对于这种应用程序,Databricks公司把它称为ContinuousApplication(实时响应数据的端到端的应用程序),也就是连续的应用程序。在Continuous Application中会有许多难点,比如数据交互的完整性、流数据与离线数据的结合使用、在线机器学习等。

Spark2.0最重磅的更新是新的流处理框架——Structured Streaming。它允许用户使用DataFrame/DataSetAPI编写与离线批处理几乎相同的代码,便可以作用到流数据和静态数据上,引擎会自动增量化流数据计算,同时保证了数据处理的一致性,并且提供了和存储系统的事务集成。

2.1.2 新的API  

在Spark2.0版本的API中共有如下几个API的变动:

1)统一了DataFrame和DataSet。现在DataFrame不再是一个独立的类,而是作为DataSet[Row]的别名定义在org.apache.spark.sql这个包对象中。

sql\package.scala源代码如下:

1.           packageobject sql {

2.          

3.           /**

4.            * Converts a logical plan into zero or moreSparkPlans.  This API is exposed forexperimenting

5.            * with the query planner and is not designedto be stable across spark releases. Developers

6.            * writing libraries should instead considerusing the stable APIs provided in

7.            * [[org.apache.spark.sql.sources]]

8.            */

9.           @DeveloperApi

10.        @InterfaceStability.Unstable

11.        type Strategy = SparkStrategy

12.       

13.        type DataFrame = Dataset[Row]

14.      }

2)加入了SparkSession用于替换DataFrame和Dataset API的SQLContext和HiveContext(这两个API仍然可以使用)。

3)为SparkSession为SparkSQL加入一个新的,精简的配置参数–RuntimeConfig,可以用来设置和获得跟SparkSQL有关的Spark或者Hadoop设置。

SparkSession.scala源代码:

1.           /**

2.            * Runtime configuration interface for Spark.

3.            *

4.            * This is the interface through which theuser can get and set all Spark and Hadoop

5.            * configurations that are relevant to SparkSQL. When getting the value of a config,

6.            * this defaults to the value set in theunderlying `SparkContext`, if any.

7.            *

8.            * @since 2.0.0

9.            */

10.        @transient lazy val conf: RuntimeConfig = newRuntimeConfig(sessionState.conf)

 

4)更简单,更高性能的累加器API。

5)用于DataSet中类型化聚合的新的改进的AggregatorAPI。

2.2   Spark2.X Core  

         本节讲解Tungsten引擎的新特性;SparkSession使用方法;以及一个更加简单和更高性能的累加器API的使用。

2.2.1 第二代Tungsten引擎

Spark备受瞩目的原因之一在于它的高性能,Spark开发者为了保持这个优势一直在不断的进行各种层次的优化,其中最令人兴奋的莫过于钨丝计划(ProjectTungsten),因为钨丝计划的提出给Spark带来了极大的性能提升,并且在一定程度上引导了Spark的发展方向。

Spark是使用Scala和Java语言开发的,不可避免的运行JVM之上,当然内存管理也是依赖于JVM的内存管理机制,而对于大数据量的基于内存的处理,JVM对象模型对内存的额外开销,以及频繁的GC和Full GC都是非常致命的问题。另外,随着网络带宽和磁盘IO的不断提升,内存和CPU又重新作为性能瓶颈受到关注,JVM对象的序列化、反序列化带来的性能损耗急需解决。Spark1.5版本加入的钨丝计划从3大方面着手解决这些问题:

1) 统一内存管理模型和二进制处理(BinaryProcessing)。统一内存管理模型来代替

之前基于JVM的静态内存管理,引入Page来管理堆内存和堆外内存(on-heap和off-heap),并且直接操作内存中的二进制数据而不是Java对象,很大程度上摆脱了JVM内存管理的限制。

2) 基于缓存感知的计算(Cache-aware Computation)。Spark内存读取操作也会带来

一部分性能损耗,钨丝计划便设计了缓存友好的算法和数据结构来提高缓存命中率,充分利用L1/L2/L3三级缓存,大幅提高了内存读取速度,进而缩短了内存中的整个计算过程的时间。

3) 代码生成(Code Generation)。在JVM中,所有代码的执行由解释器来一步步的

解释执行,CodeGeneration这一功能则在Spark运行时动态生成用于部分算子求值的bytecode,减少了对基础数据类型的封装,并且缓解了调用虚函数的额外开销。

Spark2.0升级了第二代Tungsten引擎。其中最重要的一点是把CodeGeneration作用于全阶段的SparkSQL和DataFrame之上,(即全阶段代码生成Whole Stage Code Generation),为常见的算子带来了十倍左右的性能提升!

2.2.2 SparkSession  

加入SparkSession,取代原来的SQLContext和HiveContext,为了兼容两者仍然保留。SparkSession使用方法如下:

1.           SparkSession.builder()

2.               .master("local")

3.               .appName("Word Count")

4.               .config("spark.some.config.option","some-value")

5.               .getOrCreate()

 

首先获得SparkSession的Builder,然后使用Builder来为SparkSession设置参数,最后使用getOrCreate方法来检测当前线程是否有一个已经存在的Thread-local级别的SparkSession,如果有则返回它,没有则检测是否有全局级别的SparkSession,有则返回没有则创建新的SparkSession。

在程序中如果要使用SparkContext时可以调用sparkSession.sparkContext即可。在程序的最后我们需要调用sparkContext.stop方法,这个方法会调用sparkContext.stop来关闭sparkContext。

从Spark2.0开始,DataFrame和DataSet既可以容纳静态、有限的数据,也可以容纳无限的流数据,所以用户也可以使用SparkSession像创建静态数据集一样来创建流式数据集,并且可以使用相同的操作算子。这样整合了实时流处理和离线处理的框架,结合其它容错、扩展等特性形成了完整的Lambda架构。

 

2.2.3 Accumulator API 

Spark2.0引入了一个更加简单和更高性能的累加器API,比如在1.X版本中可以这样使用累加器:

1.         //定义累加器,这里直接使用SparkContext内置的累加器,设置初始值为0,名字为"My Accumulator"

2.         val accum = sc.accumulator(0,"My Accumulator")

3.         //计算值

4.         sc.parallelize(Array(1, 2, 3,4)).foreach(x => accum += x)

5.         //获取累加器的值,(Executor上面只能对累加器进行累加操作,只有Driver才能读取累加器的值,Driver读取值的时候会把各个Executor上存储的本地累加器的值加起来),这里结果是10。

6.         accum.value

在2.X版本里使用SparkContext里内置的累加器:

1.          //与1.X不同的是需要指定累加器的类型,目前SparkContext有Long类型和Double类型的累加器可以直接使用(不需要指定初始值)。

2.         val accum =sc.longAccumulator("My Accumulator")

3.         sc.parallelize(Array(1, 2, 3,4)).foreach(x => accum.add(x))

4.         print(accum.value)

 

只使用SparkContext里内置的累加器功能肯定不能满足略微复杂的业务类型,此时我们就可以自定义累加器。在1.X版本的做法是(下面是官网的例子):

1.          //继承AccumulatorParam[Vector],返回类型为Vector。

2.         object VectorAccumulatorParamextends AccumulatorParam[Vector] {

3.         //定义“零”值,这里把传入的初始值的size作为“零”值。

4.         def zero(initialValue: Vector):Vector = {

5.             Vector.zeros(initialValue.size)

6.           }

7.         //定义累加操作的计算方式

8.           def addInPlace(v1: Vector, v2: Vector):Vector = {

9.             v1 += v2

10.        }

11.      }

 

上面的累加器元素和返回类型是相同的,在Scala中还有另外一种方式来自定义累加器,用户只需要继承Accumulable,就可以把元素和返回值定义为不同的类型,这样我们就可以完成添加操作(比如像Int类型的List里添加整数,此时元素为Int类型,而返回类型为List)。

在Spark2.X中,加入了一个新的抽象类--AccumulatorV2,继承这个类要实现几个方法:

add方法:指定元素相加操作。

copy方法:指定对自定义的累加器的拷贝操作。

isZero方法:返回该累加器的值是否为“零”。

merge方法:合并两个相同类型的累加器。

reset方法:重置累加器。

value方法:返回累加器当前的值。

     在重写这几个方法之后,只需实例化自定义累加器,并连同累加器名字一起传给sparkContext.register方法即可。

我们来简单实现一个把字符串合并为数组的累加器:

1.          //首先要继承AccumulatorV2,并指定输入为String类型,输出为ArrayBuffer[String]

2.         class MyAccumulator extendsAccumulatorV2[String, ArrayBuffer[String]] {

3.         //设置累加器的结果,类型为ArrayBuffer[String]

4.           private var result = ArrayBuffer[String]()

5.          

6.         //判断累加器当前值是否为“零值”,这里我们指定如果result的size为0则累加器的当前值是“零值”

7.           override def isZero: Boolean =this.result.size == 0

8.          

9.         //copy方法设置为新建本累加器,并把result赋给新的累加器

10.        override def copy(): AccumulatorV2[String,ArrayBuffer[String]] = {

11.          val newAccum = new MyAccumulator

12.          newAccum.result = this.result

13.          newAccum

14.        }

15.      //reset方法设置为把result设置为新的ArrayBuffer

16.      override def reset(): Unit =this.result == new ArrayBuffer[String]()

17.       

18.      //add方法是把传进来的字符串添加到result内

19.      override def add(v: String):Unit = this.result += v

20.       

21.      //merge方法:把两个累加器的result合并起来

22.      override def merge(other:AccumulatorV2[String, ArrayBuffer[String]]): Unit = {

23.            result.++=:(other.value)

24.          }

25.      //value方法返回result

26.        override def value: ArrayBuffer[String] =this.result

27.      }

28.      接着在main方法里使用累加器:

29.      val Myaccum = newMyAccumulator()

30.       

31.      //向SparkContext注册累加器

32.          sc.register(Myaccum)

33.       

34.      //把“a”,“b”“c”“d”添加进累加器的result这个数组并打印出来

35.          sc.parallelize(Array("a","b","c","d")).foreach(x=> Myaccum.add(x))

36.          println(Myaccum.value)

 

运行结果显示的ArrayBuffer里的值顺序是不固定的,取决于各个Executor的值到达Driver的顺序。

 

2.3   Spark2.X SQL   

Spark 2.0通过对SQL2003的支持增强了SQL功能,Catalyst新引擎提升了Spark查询优化的速度;本节对DataFrame和Dataset API、时间窗口进行了讲解。   

Apache Spark2.2.0版本中核心和Spark SQL的更新:

1、  API更新

·          SPARK-19107: Supportcreating hive table with DataFrameWriter and Catalog

·          SPARK-13721: Add support forLATERAL VIEW OUTER explode()

·          SPARK-18885: Unify CREATE TABLEsyntax for data source and hive serde tables

·          SPARK-16475: Added BroadcastHints BROADCAST, BROADCASTJOIN, and MAPJOIN, for SQL Queries

·          SPARK-18350: Support sessionlocal timezone

·          SPARK-19261: Support ALTERTABLE table_name ADD COLUMNS

·          SPARK-20420: Add events tothe external catalog

·          SPARK-18127: Add hooks andextension points to Spark

·          SPARK-20576: Support generichint function in Dataset/DataFrame

·          SPARK-17203: Data sourceoptions should always be case insensitive

·          SPARK-19139: AES-basedauthentication mechanism for Spark

2、  性能优化和系统稳定性

基于成本的优化:

·          SPARK-17075 SPARK-17076SPARK-19020 SPARK-17077 SPARK-19350: Cardinality estimation for filter, join,aggregate, project and limit/sample operators

·          SPARK-17080: Cost-based joinre-ordering

·          SPARK-17626: TPC-DS performanceimprovements using star-schema heuristics

·          SPARK-17949: Introduce a JVMobject based aggregate operator

·          SPARK-18186: Partialaggregation support of HiveUDAFFunction

·          SPARK-18362 SPARK-19918:File listing/IO improvements for CSV and JSON

·          SPARK-18775: Limit the maxnumber of records written per file

·          SPARK-18761: Uncancellable /unkillable tasks shouldn’t starve jobs of resources

·          SPARK-15352: Topology awareblock replication

3、  其他的一些变化

·          SPARK-18352: Support forparsing multi-line JSON files

·          SPARK-19610: Support forparsing multi-line CSV files

·          SPARK-21079: Analyze TableCommand on partitioned tables

·          SPARK-18703: Drop StagingDirectories and Data Files after completion of Insertion/CTAS againstHive-serde Tables

·          SPARK-18209: More robustview canonicalization without full SQL expansion

·          SPARK-13446: [SPARK-18112]Support reading data from Hive metastore 2.0/2.1

·          SPARK-18191: Port RDD API touse commit protocol

·          SPARK-8425:Add blacklistmechanism for task scheduling

·          SPARK-19464: Remove supportfor Hadoop 2.5 and earlier

·          SPARK-19493: Remove Java 7support

 

2.3.1 Spark SQL 

Spark 2.0通过对SQL2003的支持大幅增强了SQL功能,现在可以运行所有99个TPC-DS查询。这个版本中的SparkSQL主要有以下几点改进:

1)  引入了支持ANSISQL和HiveSQL的本地解析器。

2)  本地实现DDL命令。

3)  支持非相关标量子查询。

4)  在Where与having条件中,支持(not)in和(not)exists。

5)  即使Spark没有和Hive集成搭建,SparkSQL也支持它们一起搭建时的除了Hive连接、Hive UDF(UserDefinedFunction用户自定义函数)和脚本转换之外的大部分功能。

6)  Hive式的分桶方式的支持。

 

另外Catalyst查询优化器对于常见的工作负载也有了很多提升,对比如nullability propagation之类的查询做了更好的优化。Catalyst查询优化器从最早的应用于SparkSQL到现在应用于DataSetAPI,对Spark程序的高效率运行起到了非常重要的作用,并且随着DataSetAPI的流行,以及优化器自身的不断演进,未来肯定会对Spark的所有框架带来更高的执行效率。

2.3.2 DataFrame和Dataset API 

在Spark 1.x版本中,DataFrame的API存在很多问题,比如说DataFrame不是类型安全的(nottype-safe)、不是面向对象的(notobject-oriented),为了克服这些问题,Spark在1.6版本引入了Dataset并在2.0版本的Scala和Java中将二者进行了统一(在Python和R中,由于缺少类型安全性,DataFrame仍是主要的编程接口),DataFrame成为了DataSet[Row]的别名,而且Spark2.0版本为DataSet的类型化聚合加入了一个新的聚合器,让基于DataSet的聚合更加高效。

在2.1版本中DataFrame和Dataset API晋升为稳定的API,也就是说可以在生产环境中使用它们,且后续会基于向后兼容的前提下不断强化。

DataSetAPI是High-LevelAPI,有更高的抽象级别,与RDDAPI这样的Low-LevelAPI相比更加易用,它对于提升用户的工作效率,以及提高程序的可读性而言意义非凡。由于WholeStageCodeGeneration的引入,SparkSQL和DataSetAPI中的常见算子的性能提升了2到10倍。加上Catalyst查询优化器和Tungsten的帮助,用户可以不用过多的关注对程序优化,也能获得很好的执行效率。

所以毋庸置疑地,这样一种简单高效的API将成为Spark未来主流的编程接口!

 

2.3.3 Timed window    

对于经常用到复杂SQL的用户而言,窗口函数一直以来都是不可或缺的,在Spark2.X版本中通过对Hive中的窗口函数的本地化实现,来使用spark的内存管理机制,从而提升了窗口函数的性能。

2.4 Spark 2.X Streaming 

Spark2.0为我们带来了一个新的流处理框架Structured Streaming,这是一个基于Spark SQL和Catalyst优化器构建的高级流API。它允许用户使用与操作静态数据的DataFrame / Dataset API对流数据进行编程,利用Catalyst优化器自动地增量化查询计划。并且它不但支持流数据的不断写入,还支持其他的静态数据的插入。

Apache Spark2.2.0版本中Structured Streaming的更新:

1、    整体可用性:

·          SPARK-20844: The StructuredStreaming APIs are now GA and is no longer labeled experimental

2、  Kafka 提升

·          SPARK-19719: Support forreading and writing data in streaming or batch to/from Apache Kafka

·          SPARK-19968: Cached producerfor lower latency kafka to kafka streams.

3、    API 更新:

·          SPARK-19067: Support forcomplex stateful processing and timeouts using [flat]MapGroupsWithState

·          SPARK-19876: Support for onetime triggers

4、    其它的一些变化:

·          SPARK-20979: Rate source fortesting and benchmarks

 

你可能感兴趣的:(SparkInBeiJing)