引言
根据IBM前首席执行官郭士纳的观点,IT领域每隔十五年就会迎来一 次重大变革 。当前我们正处于第三次信息浪潮(2010年前后),物联网、云计算和大数据技术突飞猛进。 信息爆炸是我们当前所需要解决的主要问题。
思维方式:
- 分析使用全量样本,而非抽样后进行分析 ;
- 更注重处理效率,而非精确度 ;
- 丰富的数据源与分析目标相关,而不具有直接的因果关系。
本文从大数据背景出发,结合林子雨老师的课程,尝试系统地归纳、总结出Spark编程所需了解的架构基础以及程序运行原理。更有利于理解后续编程内容。后续文章会依次按照以下顺序展开:
- Spark架构基础与运行原理
- RDD编程
- Spark SQL
- Spark Streaming
- Spark MLlib
⭐️言归正传,本节主要聚焦于大数据技术的基础架构与运行原理。内容概览如下。
引言
1.关键技术
2.核心功能
3.计算模式
4.使用Spark的原因
7.Spark运行流程
8.弹性分布数据集(RDD)
9.Shuffle与依赖
10.Spark的部署方式
总结
1.关键技术
⭐️当前大数据技术所涉及的技术层面有4个,其功能如下:
1)数据采集
利用ETL工具将分布的、异构数据源中的数据如关系数据、平 面数据文件等,抽取到临时中间层后进行清洗、转换、集成, 最后加载到数据仓库或数据集市中,成为联机分析处理、数据 挖掘的基础;或者也可以把实时采集的数据作为流计算系统的 输入,进行实时处理分析。
2)数据存储和管理
利用分布式文件系统、数据仓库、关系数据库、NoSQL数据库 、云数据库等,实现对结构化、半结构化和非结构化海量数据 的存储和管理。
3)数据处理与分析
利用分布式并行编程模型和计算框架,结合机器学习和数据挖 掘算法,实现对海量数据的处理和分析;对分析结果进行可视 化呈现,帮助人们更好地理解数据、分析数据。
4)数据隐私和安全
在从大数据中挖掘潜在的巨大商业价值和学术价值的同时,构 建隐私数据保护体系和数据安全体系,有效保护个人隐私和数据安全。
2.核心功能
⭐️为实现以上层面的功能,大数据架构中需包含以下两个核心功能:
1)分布式存储
分布式存储方式包括:
GFSHDFS
BigTableHBase
NoSQL(键值、列族、图形、文档数据库)
NewSQL(如:SQL Azure)
等等。
2)分布式处理
Hadoop生态通过MapReduce实现数据的分布式处理,而Spark即是用来代替MapReduce的一种更高效的组件。Spark与Hadoop并不是对等关系。所以传言说,Spark会取代Hadoop是一种错误的说法 。Spark只是代替了MapReduce的分布式处理,而另一个非常重要的部分——分布式存储,目前主流框架仍是基于Hadoop生态中的HDFS组件的。
3.计算模式
⭐️现阶段大数据计算模式以及其代表产品:
1)批处理计算
批处理计算可以实现对大规模数据的批量处理。MapReduce、Spark等就是为了该任务而设计的。
2)流计算
流计算需要较高的响应速度。因此对计算的效率要求更高于其精确度。Storm、S4、Flume、Streams 、Puma、DStream、Super Mario、银河流数据处理平台等,都可以针对流数据的实时计算。
3)图计算
Pregel、GraphX、Giraph、 PowerGraph、Hama、 GoldenOrb等,可针对大规模图结构数据进行处理。
4)查询分析计算
对于企业中的业务分析人员,通常只需要针对大规模数据的实现存储管理,或者进行查询分析。常用的产品如Hive、Dremel、Cassandra、 Impala等。
Hadoop生态系统的架构图如下所示。
以HDFS为基础,通过YARN来管理和调度集群资源,最终通过MapReduce实现分布式计算。而上层的Hive、Pig、Mahout等通过更简单的语言编译为MapReduce语句,给用户以更好的交互体验以及更低的使用门槛。
为了更好的理解Spark运行机制,首先要理解分布式计算的两个核心:
1)MapReduce
- MapReduce将复杂的、运行于大规模集群上的并行计算过程高度地抽象到了两个函数: Map和Reduce 。
- 编程非常容易,不需要掌握分布式并行编程细节,也可以很容易把自己的程序运行在分布式 系统上,完成海量数据的计算 。
- MapReduce采用“分而治之”策略,一个存储在分布式文件系统中的大规模数据集,会 被切分成许多独立的分片(split),这些分片可以被多个Map任务并行处理。
2)YARN
- YARN的目标就是实现“一个集群多个框架”,即在一个集群上部署一个统 一的资源调度管理框架YARN,在YARN之上可以部署其他各种计算框架 ;
- 由YARN为这些计算框架提供统一的资源调度管理服务,并且能够根据各种 计算框架的负载需求,调整各自占用的资源,实现集群资源共享和资源弹性 收缩;
- 可以实现一个集群上的不同应用负载混搭,有效提高了集群的利用率 ;
- 不同计算框架可以共享底层存储,避免了数据集跨集群移动 。
下图展示了在YARN上部署各种计算框架:
对于YARN暂时不需要过多关注,只需要记住,在分布式计算前,会先经过YARN调配当前平台的各种计算资源,以防止某个任务占用过多资源或资源浪费。
4.使用Spark的原因
⭐️那么为什么使用Spark呢?
Hadoop存在如下一些缺点:
- 表达能力有限
- 磁盘IO开销大
- 延迟高
Hadoop中任务之间的衔接涉及IO开销。在前一个任务执行完成之前,其他任务就无法 开始,难以胜任复杂、多阶段的计算任务。尤其是机器学习所涉及的迭代计算。在MapReduce框架下效率极低。
Spark在借鉴Hadoop MapReduce优点的同时,很好地解决了 MapReduce所面临的问题 相比于Hadoop MapReduce,Spark主要具有如下优点 :
- Spark的计算模式也属于MapReduce,但不局限于Map和Reduce操作 ,还提供了多种数据集操作类型,编程模型比Hadoop MapReduce更 灵活
- Spark提供了内存计算,可将中间结果放到内存中,对于迭代运算 效率更高
- Spark基于DAG的任务调度执行机制,要优于Hadoop MapReduce的 迭代执行机制
Spark将数据载入内存后,之后的迭代计算都可以直接使用内存中的中间结果作运算,避免了从磁盘中频繁读取数据。Hadoop与Spark执行逻辑回归的时间对比如下图所示:
Spark有选择的将数据放入内存中或磁盘中,自动地寻找最佳计算方法。至于其计算方法,在后续文章中进行介绍。
5.基本概念
⭐️RDD:是Resillient Distributed Dataset(弹性分布式数据集)的简称,是分布 式内存的一个抽象概念,提供了一种高度受限的共享内存模型
DAG:是Directed Acyclic Graph(有向无环图)的简称,反映RDD之间的依 赖关系
Executor:是运行在工作节点(WorkerNode)的一个进程,负责运行Task
应用(Application):用户编写的Spark应用程序
任务( Task ):运行在Executor上的工作单元
作业( Job ):一个作业包含多个RDD及作用于相应RDD上的各种操作
阶段( Stage ):是作业的基本调度单位,一个作业会分为多组任务,每 组任务被称为阶段,或者也被称为任务集合,代表了一组关联的、相互之间 没有Shuffle依赖关系的任务组成的任务集
6.架构设计
⭐️Spark运行架构包括集群资源管理器(Cluster Manager)、运行作 业任务的工作节点(Worker Node)、每个应用的任务控制节点 (Driver)和每个工作节点上负责具体任务的执行进程(Executor)。资源管理器可以自带或Mesos或YARN 。
在Spark中,一个应用(Application)由一个任务控制节点(Driver)和若干个作业(Job)构成,一个作业由多个阶段(Stage)构成,一个阶段由多个任务(Task)组成。当执行一个应用时,任务控制节点会向集群管理器(Cluster Manager)申请资源,启动Executor,并向Executor发送应用程序代码和文件,然后在Executor上执行任务,运行结束后,执行结果会返回给任务控制节点,或者写到HDFS或者其他数据库中。
7.Spark运行流程
⭐️接下来看一下Spark运行的4个步骤:
- 当一个Spark应用被提交时,Driver创建一个SparkContext,由SparkContext负责和资源管理器(Cluster Manager)的通信以及进行资源的申请、任务的分配和监控等。SparkContext会向资源管理器注册并申请运行Executor的资源 ;
- 资源管理器为Executor分配资源,并启动Executor进程,Executor发送心跳到资源管理器上;
- SparkContext根据RDD的依赖关系构建DAG图,DAG图提交给DAG调度器(DAGScheduler)进行解析,将DAG图分解成多个“阶段”,并且计算出各个阶段之间的依赖关系,然后把一个个“任务集”提交给底层的任务调度器(TaskScheduler)进行处理;Executor向SparkContext申请任务,任务调度器将任务分发给Executor运行,同时,SparkContext将应用程序代码发放给Executor;
- 任务在Executor上运行,把执行结果反馈给任务调度器,然后反馈给DAG调度器,运行完毕后写入数据并释放所有资源 。
该过程的特点:
- 数据本地化,计算向数据靠拢;
- 多线程方式,executor执行task的时候采用多线程方式,减少了多进程任务频繁的启动开销;
- BlockManager存储模块,存储中间结果。
8.弹性分布数据集(RDD)
⭐️一个RDD就是一个分布式对象集合,本质上是一个只读的分区记录集合,每个RDD可以分成多个分区,每个分区就是一个数据集片段,并且一个RDD的不同分区可以被保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算。
RDD提供了一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,不能直接修改,只能基于稳定的物理存储中的数据集来创建RDD,或者通过在其他RDD上执行确定的转换操作(如map、join和groupBy)而创建得到新的RDD。
RDD提供了一组丰富的操作以支持常见的数据运算 ,分为 “动作”(Action)和“转换”(Transformation)两种类型。其中Transformation有两种:
- 转换操作:指定RDD之间的相互依赖关系,输入RDD,输出RDD,不立即执行。比如map,filter等;
- 行动操作:用于执行计算并指定输出的形式,输入RDD,返回非RDD。比如count,collect等。
RDD的算子是惰性调用。只有第一次在一个行动操作中用到时,才会真正计算 。RDD具有如下特性:
- 只读——只能新生成RDD,而不能修改原始RDD,因此具有高效的容错性;
- 中间结果持久化到内存,不需要“落地”到磁盘上,避免了不必要的读写磁盘开销;
- 存放的数据可以是Java对象,避免了不必要的对 象序列化和反序列化。
通常把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。对象的序列化主要有两种用途:
- 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
- 在网络上传送对象的字节序列。
9.Shuffle与依赖
⭐️Spark通过分析各个RDD的依赖关系生成了DAG,再通过分析各个RDD中的分区之间的依赖关系来决定如何划分阶段。
在Spark中,有两种依赖关系:
- 宽依赖:一个父RDD的一个分区对应一个子RDD的多个分区;
- 窄依赖:一个父RDD的分区对应于一个子RDD的分区,或多个父RDD的分区对应于一个子RDD的分区。
Shuffle操作。
Spark 根据DAG 图中的RDD 依赖关系,把一个作业分成多个 阶段。阶段划分的依据是窄依赖和宽依赖。对于宽依赖和窄依 赖而言,窄依赖对于作业的优化很有利,宽依赖无法优化。逻辑上,每个RDD 操作都是一个fork/join(一种用于并行执行 任务的框架),把计算fork 到每个RDD 分区,完成计算后对 各个分区得到的结果进行join 操作,然后fork/join下一个RDD 操作。
窄依赖可以实现“流水线”优化。宽依赖无法实现“流水线”优化。
上图中,A->B存在一个父RDD的一个分区对应一个子RDD的多个分区,因此是一个宽依赖,不能优化。
C->D->F显然是一个窄依赖,可以进行优化。
B->G是一个窄依赖,可以进行优化。
F->G是一个宽依赖,不能进行优化。
⭐️前文提到,Spark通过分析各个RDD的依赖关系生成了DAG,再通过分 析各个RDD中的分区之间的依赖关系来决定如何划分 Stage,具体划分方法是:
- 在DAG中进行反向解析,遇到宽依赖就断开 ;
- 遇到窄依赖就把当前的RDD加入到Stage中 ;
- 将窄依赖尽量划分在同一个Stage中,可以实现流水线计算。
比如下图中,流水线操作实例分区7通过map操作生成的分区9,可以不用等待 区8到分区10这个map操作的计算结束,而是继续进行union操作,得到分区13,这样流水线执行大大提高了计算的效率。
⭐️通过上述对RDD概念、依赖关系和Stage划分的介绍,结合之前介绍的Spark运行基 本流程,再总结一下RDD在Spark架构中的运行过程:
- 创建RDD对象;
- SparkContext负责计算RDD之间的依赖关系,构建DAG;
- DAGScheduler负责把DAG图分解成多个Stage,每个Stage中包含了多个 Task,每个Task会被TaskScheduler分发给各个WorkerNode上的Executor去执行。
10.Spark的部署方式
⭐️Spark支持三种不同类型的部署方式,包括:
- Standalone(类似于MapReduce1.0,slot为资源分配单 位)
- Spark on Mesos(和Spark有血缘关系,更好支持Mesos)
- Spark on YARN
Spark on YARN架构
Hadoop现在分三块HDFS/MR/YARN,Spark比Hadoop性能好,只是Spark作为一个计算引擎,比MR的性能要好。但它的存储和调度框 架还是依赖于HDFS/YARN,Spark也有自己的调度框架,但仍然非常 不成熟,基本不可商用。