小编说:随着大数据技术的发展,实时流计算、机器学习、图计算等领域成为较热的研究方向,而Spark作为大数据处理的“利器”有着较为成熟的生态圈,能够一站式解决类似场景的问题。正是Spark这个生态系统实现了“One Stack to Rule Them All”目标。 本文选自《图解Spark:核心技术与案例实战》
Spark 生态系统以Spark Core 为核心,能够读取传统文件(如文本文件)、HDFS、Amazon S3、Alluxio 和NoSQL 等数据源,利用Standalone、YARN 和Mesos 等资源调度管理,完成应用程序分析与处理。这些应用程序来自Spark 的不同组件,如Spark Shell 或Spark Submit 交互式批处理方式、Spark Streaming 的实时流处理应用、Spark SQL 的即席查询、采样近似查询引擎BlinkDB 的权衡查询、MLbase/MLlib 的机器学习、GraphX 的图处理和SparkR 的数学计算等,如下图所示,正是这个生态系统实现了“One Stack to Rule Them All”目标。
- Spark Core
Spark Core 是整个BDAS 生态系统的核心组件,是一个分布式大数据处理框架。Spark Core提供了多种资源调度管理,通过内存计算、有向无环图(DAG)等机制保证分布式计算的快速,并引入了RDD 的抽象保证数据的高容错性,其重要特性描述如下。
· Spark Core 提供了多种运行模式,不仅可以使用自身运行模式处理任务,如本地模式、Standalone,而且可以使用第三方资源调度框架来处理任务,如YARN、MESOS 等。
相比较而言,第三方资源调度框架能够更细粒度管理资源。
· Spark Core 提供了有向无环图(DAG)的分布式并行计算框架,并提供内存机制来支持多次迭代计算或者数据共享,大大减少迭代计算之间读取数据的开销,这对于需要进行多次迭代的数据挖掘和分析性能有极大提升。另外,在任务处理过程中移动计算而非移动数据,RDD Partition 可以就近读取分布式文件系统中的数据块到各个节点内存中进行计算。
· 在Spark 中引入了RDD 的抽象,它是分布在一组节点中的只读对象集合,这些集合是弹性的,如果数据集一部分丢失,则可以根据“血统”对它们进行重建,保证了数据的高容错性。
- Spark Streaming
Spark Streaming 是一个对实时数据流进行高吞吐、高容错的流式处理系统,可以对多种数据源(如Kafka、Flume、Twitter 和ZeroMQ 等)进行类似Map、Reduce 和Join 等复杂操作,并将结果保存到外部文件系统、数据库或应用到实时仪表盘,如下图。
相比其他的处理引擎要么只专注于流处理,要么只负责批处理(仅提供需要外部实现的流处理API 接口),而Spark Streaming 最大的优势是提供的处理引擎和RDD 编程模型可以同时进行批处理与流处理。
对于传统流处理中一次处理一条记录的方式而言,Spark Streaming 使用的是将流数据离散化处理(Discretized Streams),通过该处理方式能够进行秒级以下的数据批处理。在SparkStreaming 处理过程中,Receiver 并行接收数据,并将数据缓存至Spark 工作节点的内存中。经过延迟优化后,Spark 引擎对短任务(几十毫秒)能够进行批处理,并且可将结果输出至其他系统中。与传统连续算子模型不同,其模型是静态分配给一个节点进行计算,而Spark 可基于数据的来源以及可用资源情况动态分配给工作节点。
使用离散化流数据(DStreaming),Spark Streaming 将具有如下特性。
· 动态负载均衡:Spark Streaming 将数据划分为小批量,通过这种方式可以实现对资源更细粒度的分配。例如,传统实时流记录处理系统在输入数据流以键值进行分区处理情况下,如果一个节点计算压力较大超出了负荷,该节点将成为瓶颈,进而拖慢整个系统的处理速度。而在Spark Streaming 中,作业任务将会动态地平衡分配给各个节点,如图,即如果任务处理时间较长,分配的任务数量将少些;如果任务处理时间较短,则分配的任务数据将更多些。
· 快速故障恢复机制:在节点出现故障的情况下,传统流处理系统会在其他的节点上重启失败的连续算子,并可能重新运行先前数据流处理操作获取部分丢失数据。在此过程中只有该节点重新处理失败的过程,只有在新节点完成故障前所有计算后,整个系统才能够处理其他任务。在Spark 中,计算将分成许多小的任务,保证能在任何节点运行后能够正确进行合并。因此,在某节点出现的故障的情况,这个节点的任务将均匀地分散到集群中的节点进行计算,相对于传递故障恢复机制能够更快地恢复。
批处理、流处理与交互式分析的一体化:Spark Streaming 是将流式计算分解成一系列短小的批处理作业,也就是把Spark Streaming 的输入数据按照批处理大小(如几秒)分成一段一段的离散数据流(DStream),每一段数据都转换成Spark 中的RDD,然后将Spark Streaming 中对DStream 流处理操作变为针对Spark 中对RDD 的批处理操作。另外,流数据都储存在Spark 节点的内存里,用户便能根据所需进行交互查询。正是利用了Spark 这种工作机制将批处理、流处理与交互式工作结合在一起。
- Spark SQL
Spark SQL 的前身是Shark,它发布时Hive 可以说是SQL on Hadoop 的唯一选择(Hive 负责将SQL 编译成可扩展的MapReduce 作业),鉴于Hive 的性能以及与Spark 的兼容,Shark 由此而生。
Shark 即Hive on Spark,本质上是通过Hive 的HQL 进行解析,把HQL 翻译成Spark 上对应的RDD 操作,然后通过Hive 的Metadata 获取数据库里的表信息,实际为HDFS 上的数据和文件,最后由Shark 获取并放到Spark 上运算。Shark 的最大特性就是速度快,能与Hive 的完全兼容,并且可以在Shell 模式下使用rdd2sql 这样的API,把HQL 得到的结果集继续在Scala环境下运算,支持用户编写简单的机器学习或简单分析处理函数,对HQL 结果进一步分析计算。
在2014 年7 月1 日的Spark Summit 上,Databricks 宣布终止对Shark 的开发,将重点放到Spark SQL 上。在此次会议上,Databricks 表示,Shark 更多是对Hive 的改造,替换了Hive 的物理执行引擎,使之有一个较快的处理速度。然而,不容忽视的是,Shark 继承了大量的Hive代码,因此给优化和维护带来大量的麻烦。随着性能优化和先进分析整合的进一步加深,基于MapReduce 设计的部分无疑成为了整个项目的瓶颈。因此,为了更好的发展,给用户提供一个更好的体验,Databricks 宣布终止Shark 项目,从而将更多的精力放到Spark SQL 上。
Spark SQL 允许开发人员直接处理RDD,同时也可查询在 Hive 上存在的外部数据。SparkSQL 的一个重要特点是能够统一处理关系表和RDD,使得开发人员可以轻松地使用SQL 命令进行外部查询,同时进行更复杂的数据分析。
Spark SQL 的特点如下:
· 引入了新的RDD 类型SchemaRDD,可以像传统数据库定义表一样来定义SchemaRDD。
SchemaRDD 由定义了列数据类型的行对象构成。SchemaRDD 既可以从RDD 转换过
来,也可以从Parquet 文件读入,还可以使用HiveQL 从Hive 中获取。
· 内嵌了Catalyst 查询优化框架,在把SQL 解析成逻辑执行计划之后,利用Catalyst 包
里的一些类和接口,执行了一些简单的执行计划优化,最后变成RDD 的计算。
· 在应用程序中可以混合使用不同来源的数据,如可以将来自HiveQL的数据和来自SQL的数据进行Join 操作。
Shark 的出现使得SQL-on-Hadoop 的性能比Hive 有了10~100 倍的提高,那么,摆脱了
Hive 的限制,Spark SQL 的性能又有怎么样的表现呢?虽然没有Shark 相对于Hive 那样瞩目的性能提升,但也表现得优异,如图(其中,右侧数据为Spark SQL)。
为什么Spark SQL 的性能会得到这么大的提升呢?主要是Spark SQL 在以下几点做了优化。
· 内存列存储(In-Memory Columnar Storage):Spark SQL 的表数据在内存中存储不是采用原生态的JVM 对象存储方式,而是采用内存列存储。
· 字节码生成技术(Bytecode Generation):Spark 1.1.0 在Catalyst 模块的Expressions 增加了Codegen 模块,使用动态字节码生成技术,对匹配的表达式采用特定的代码动态编译。另外对SQL 表达式都做了CG 优化。CG 优化的实现主要还是依靠Scala 2.10运行时的反射机制(Runtime Reflection)。
· Scala 代码优化:Spark SQL 在使用Scala 编写代码的时候,尽量避免低效的、容易GC的代码;尽管增加了编写代码的难度,但对于用户来说接口统一。
- BlinkDB
BlinkDB 是一个用于在海量数据上运行交互式SQL 查询的大规模并行查询引擎,它允许用户通过权衡数据精度来提升查询响应时间,其数据的精度被控制在允许的误差范围内。为了达到这个目标,BlinkDB 使用如下核心思想:
· 自适应优化框架,从原始数据随着时间的推移建立并维护一组多维样本。
· 动态样本选择策略,选择一个适当大小的示例,该示例基于查询的准确性和响应时间的紧迫性。
和传统关系型数据库不同,BlinkDB 是一个交互式查询系统,就像一个跷跷板,用户需要在查询精度和查询时间上做权衡;如果用户想更快地获取查询结果,那么将牺牲查询结果的精度;反之,用户如果想获取更高精度的查询结果,就需要牺牲查询响应时间。下图 为BlinkDB架构。
- MLBase/MLlib
MLBase 是Spark 生态系统中专注于机器学习的组件,它的目标是让机器学习的门槛更低,让一些可能并不了解机器学习的用户能够方便地使用MLBase。MLBase 分为4 个部分:MLRuntime、MLlib、MLI 和ML Optimizer。
· MLRuntime:是由Spark Core 提供的分布式内存计算框架,运行由Optimizer 优化过的算法进行数据的计算并输出分析结果。
· MLlib:是Spark 实现一些常见的机器学习算法和实用程序,包括分类、回归、聚类、协同过滤、降维以及底层优化。该算法可以进行可扩充。
· MLI:是一个进行特征抽取和高级ML 编程抽象算法实现的API 或平台。
· ML Optimizer:会选择它认为最适合的已经在内部实现好了的机器学习算法和相关参数,来处理用户输入的数据,并返回模型或其他帮助分析的结果。
MLBase 的核心是其优化器(ML Optimizer),它可以把声明式的任务转化成复杂的学习计划,最终产出最优的模型和计算结果。MLBase 与其他机器学习Weka 和Mahout 不同,三者各有特色,具体内容如下。
· MLBase 基于Spark,它是使用的是分布式内存计算的;Weka 是一个单机的系统,而Mahout 是使用MapReduce 进行处理数据(Mahout 正向使用Spark 处理数据转变)。
· MLBase 是自动化处理的;Weka 和Mahout 都需要使用者具备机器学习技能,来选择自己想要的算法和参数来做处理。
· MLBase 提供了不同抽象程度的接口,可以由用户通过该接口实现算法的扩展。
- GraphX
GraphX 最初是伯克利AMP 实验室的一个分布式图计算框架项目,后来整合到Spark 中成为一个核心组件。它是Spark 中用于图和图并行计算的API,可以认为是GraphLab 和Pregel 在Spark 上的重写及优化。跟其他分布式图计算框架相比,GraphX 最大的优势是:在Spark 基础上提供了一栈式数据解决方案,可以高效地完成图计算的完整的流水作业。
GraphX 的核心抽象是Resilient Distributed Property Graph,一种点和边都带属性的有向多重图。GraphX 扩展了Spark RDD 的抽象,它有Table 和Graph 两种视图,但只需要一份物理存储,两种视图都有自己独有的操作符,从而获得了灵活操作和执行效率。GraphX 的整体架构中大部分的实现都是围绕Partition 的优化进行的,这在某种程度上说明了,点分割的存储和相应的计算优化的确是图计算框架的重点和难点。
GraphX 的底层设计有以下几个关键点。
(1)对Graph 视图的所有操作,最终都会转换成其关联的Table 视图的RDD 操作来完成。这样对一个图的计算,最终在逻辑上,等价于一系列RDD 的转换过程。因此,Graph 最终具备了RDD 的3 个关键特性:Immutable、Distributed 和Fault-Tolerant。其中最关键的是Immutable(不变性)。逻辑上,所有图的转换和操作都产生了一个新图;物理上,GraphX 会有一定程度的不变顶点和边的复用优化,对用户透明。
(2)两种视图底层共用的物理数据,由RDD[Vertex-Partition]和RDD[EdgePartition]这两个RDD 组成。点和边实际都不是以表Collection[tuple] 的形式存储的, 而是由VertexPartition/EdgePartition 在内部存储一个带索引结构的分片数据块,以加速不同视图下的遍历速度。不变的索引结构在RDD 转换过程中是共用的,降低了计算和存储开销。
(3)图的分布式存储采用点分割模式,而且使用partitionBy 方法,由用户指定不同的划分策略(PartitionStrategy)。划分策略会将边分配到各个EdgePartition,顶点Master 分配到各个VertexPartition,EdgePartition 也会缓存本地边关联点的Ghost 副本。划分策略的不同会影响到所需要缓存的Ghost 副本数量,以及每个EdgePartition 分配的边的均衡程度,需要根据图的结构特征选取最佳策略。
- SparkR
R 是遵循GNU 协议的一款开源、免费的软件,广泛应用于统计计算和统计制图,但是它只能单机运行。为了能够使用R 语言分析大规模分布式的数据,伯克利分校AMP 实验室开发了SparkR,并在Spark 1.4 版本中加入了该组件。通过SparkR 可以分析大规模的数据集,并通过R Shell 交互式地在SparkR 上运行作业。SparkR 特性如下:
· 提供了Spark 中弹性分布式数据集(RDDs)的API,用户可以在集群上通过R Shell交互性地运行Spark 任务。
· 支持序化闭包功能,可以将用户定义函数中所引用到的变量自动序化发送到集群中其他的机器上。
· SparkR 还可以很容易地调用R 开发包,只需要在集群上执行操作前用includePackage读取R 开发包就可以了。
下为SparkR 的处理流程示意图。
- Alluxio
Alluxio 是一个分布式内存文件系统,它是一个高容错的分布式文件系统,允许文件以内存的速度在集群框架中进行可靠的共享,就像Spark 和 MapReduce 那样。Alluxio 是架构在最底层的分布式文件存储和上层的各种计算框架之间的一种中间件。其主要职责是将那些不需要落地到DFS 里的文件,落地到分布式内存文件系统中,来达到共享内存,从而提高效率。同时可以减少内存冗余、GC 时间等。
和Hadoop 类似,Alluxio 的架构是传统的Master-Slave 架构,所有的Alluxio Worker 都被Alluxio Master 所管理,Alluxio Master 通过Alluxio Worker 定时发出的心跳来判断Worker 是否已经崩溃以及每个Worker 剩余的内存空间量,为了防止单点问题使用了ZooKeeper 做了HA。
Alluxio 具有如下特性。
· AVA-Like File API:Alluxio 提供类似Java File 类的API。
· 兼容性:Alluxio 实现了HDFS 接口,所以Spark 和MapReduce 程序不需要任何修改即可运行。
· 可插拔的底层文件系统:Alluxio 是一个可插拔的底层文件系统,提供容错功能,它将内存数据记录在底层文件系统。它有一个通用的接口,可以很容易地插入到不同的底层文件系统。目前支持HDFS、S3、GlusterFS 和单节点的本地文件系统,以后将支持更多的文件系统。Alluxio 所支持的应用如下。