[完]Spark 基础知识

为了使程序运行更快,Spark提供了内存计算,减少了迭代计算时的IO开销;为了使编程更容易,Spark使用简练、优雅的Scala语言编写,基于Scala提供了交互式编程体验。与Hadoop相比,Spark使用十分之一的计算资源,可获得比Hadoop快3倍的速度。

一、Spark与Hadoop

Spark特点:

  • 运行速度快:采用DAG(Directed Acyclic Graph,有向无环图)执行引擎,以支持循环数据流与内存计算,基于内存的执行速度可比Hadoop MR快上百倍,基于磁盘的速度也能快十倍。
  • 容易使用:支持Scala、Java、Python和R语言进行编程。
  • 通用性:提供完整而强大的技术栈,包括SQL查询,流失计算、机器学习和图算法组件。
  • 运行模式多样:可运行于独立的集群模式中、Hadoop中、也可运行在Amazon EC2云环境中,可访问HDFS、Cassandra、HBase、Hive等数据源。

Scala是一种现代的多范式编程语言,集成了面向对象和函数式语言的特性;从小脚本到建立大系统的编程任务均可胜任;运行在JVM上,兼容Java程序,能融合到Hadoop生态圈;具有强大的并发性,支持分布式系统。

Hadoop缺点:

  • 表达能力有限。计算都要转换成Map和Reduce操作,难以描述复杂的数据处理过程。
  • 磁盘IO开销大。每次执行都需从磁盘读数据,计算过程中,需要将中间结果写入磁盘。
  • 延迟高。一次计算分解成一些列按顺序执行的MR任务,任务间衔接涉及IO开销,产生较高延迟,且一个任务完成前,其他任务无法开始。难胜任复杂、多阶段计算任务。

Spark与Hadoop相比:

  • 其计算模式也属于MR,但不局限与此,还提供多种数据集操作类型(RDD操作),编程模型更加灵活;
  • Spark提供内存计算,中间结果放在内存中,IO开销,延迟低,拥有更高地迭代运算效率;
  • 基于DAG任务调度执行机制,由于MR迭代执行机制。
  • 使用Hadoop需要编写不少相对底层的代码,而Spark提供高层次、简洁的API。
  • Spark主要替代Hadoop中的MR,而不能完全替代Hadoop,它很好地融入了Hadoop生态圈,可借助于YARN实现资源调度管理,借助HDFS实现分布式存储。
  • Hadoop可使用廉价、异构的机器实现分布式存储和计算,而Spark对硬件(内存、CPU)要求稍高。

二、Spark生态系统

Spark的设计遵循“一个软件栈满足不同应用场景”的理念。其生态系统主要包含Spark Core、Spark SQL、Spark Streaming、MLLib和GraphX等组件。

  • Spark Core:包含了Spark的基本功能,如内存计算、任务调度、部署模式、故障恢复、存储管理等。Spark建立在统一的抽象RDD之上,使其可以基本一致的方式应对不同的大数据处理场景。通常所说的Apache Spark,就是指Spark Core;
  • Spark SQL:它允许开发人员直接处理RDD,也可查询Hive、HBase等外部数据源。它的一个重要特点是能够统一处理关系表和RDD,使开发人员使用SQL命令进行查询,并进行复杂的数据分析;
  • Spark Streaming:支持高吞吐量、可容错处理的实时流数据处理,其核心思路是将流式计算分解成一系列短小的批处理作业。支持多种数据输入源,如Kafka、Flume、和TCP套接字。
  • MLlib(机器学习):提供常用的机器学习算法,含聚类、分类、回归、协同过滤。
  • GraphX(图计算):是Spark中用于图计算的API,可认为是Pregel在Spark上的重写和优化。

表 Spark应用场景

应用场景 时间跨度 其他框架 Spark生态系统中的组件
复杂的批量数据处理 小时级 MapReduce、Hive Spark
基于历史数据的交互式查询 分钟级、秒级 Impala、Dremel、Drill Spark SQL
基于实时数据流的数据处理 毫秒级、秒级 Storm、S4 Spark Streaming
基于历史数据的数据挖掘 Mahout MLLib
图结构数据的处理 Pregel、Hama GraphX

三、Spark运行框架

3-1 基本概念

  • RDD:Resillient Distributed DataSet,弹性分布式数据集,是分布式内存的一个抽象概念,提供一种高度受限的共享内存模型;
  • DAG:Directed Acyclic Graph,有向无环图,反映RDD之间的依赖关系;
  • Executor:运行在工作节点(Worker Node)上的一个进程,负责运行Task;
  • Application:用户编写的Spark应用程序;
  • Job:一个Job含多个RDD及作用于RDD上的各种操作;
  • Stage:是Job的基本调度单位,一个Job分为多组Task,每组Task被称为Stage,或者称为TaskSet,代表了一组关联的、相互之间没有Shuffle依赖关系的任务组成的任务集;
  • Task:运行在Executor上的工作单元。

3-2 架构设计

Spark运行架构包括:

  • 集群资源管理器(Cluster Manager),可以是Spark自带的资源管理器,也可是YARN、Mesos等资源管理框架;
  • 工作节点(WorkNode):运行作业任务;
  • 每个应用的任务控制节点(Driver)
  • 每个工作节点上负责具体任务的执行进程(Executor)

    Spark运行框架
    图 Spark运行框架

与Hadoop MR计算框架相比,Spark采用Executor有两个优点:

  • 利用多线程来执行具体任务(MR采用进程模型),减少任务的启动开销;
  • Executor中有一个BlockManager存储模块(类似于KV系统),会将内存和磁盘共同作为存储设备,需要多轮迭代时,将中间结果存储到此存储模块中,下次需要时,直接读取其中的数据,不需读写到HDFS文件系统中,减少IO开销;在交互式查询场景下,预先将表缓存到该存储系统上,提高读写IO性能。

扫描二维码关注公众号,回复: 13460806 查看本文章

Spark中各种概念之间的相互关系
图 Spark中各种概念之间的相互关系

在Spark中,一个Application由一个Driver和若干个Job构成,一个Job由多个Stage构成构成,一个Stage由多个没有Shuffle关系的Task组成。当执行Application时,Driver会向集群资源管理器申请资源,启动Executor,并向Executor发送应用程序代码和文件,然后在Executor上执行Task,运行结束后,执行结果返回给Driver,或者写到HDFS或者其他数据库中。

3-3 Spark运行流程

  • 提交一个Application后,首先构建基本运行环境,即由Driver创建一个SparkContext,由SparkContext负责和资源管理器(Cluster Manager)的通信,以及资源的申请、任务的分配和监控等。SparkContext会向资源管理器注册并申请运行Executor的资源;
  • 资源管理器为Executor分配资源,启动Executor进程,Executor运行情况随着“心跳”发送到资源管理器上;
  • SparkContext根据RDD的依赖关系构建DAG图,DAG图提交给DAGScheduler进行解析,将DAG图分解成Stage,并计算Stage间的依赖关系,然后将一个个TaskSet(即Stage)提交给底层调度器TaskScheduler进行处理;Executor向SparkContext申请Task,TaskScheduler将Task发放给Executor运行,同时,SparkContext将应用程序代码发送给Executor;
  • Task在Executor上运行,结果反馈给TaskScheduler,然后反馈给DAGScheduler,运行完毕后写入数据,并释放资源。

Spark运行架构具有以下特点:

  • 每个Application都有专属的Executor进程,并且在Application运行期间一直驻留。Executor进程以多线程的方式运行Task;
  • Spark运行过程与资源管理器无关,只要能获取Executor进程并保存通信即可;
  • Task采用数据本地性和推测执行等优化机制。数据本地性,即计算向数据靠拢,因为移动计算比移动数据占得网络资源要少。Spark采用延时调度机制,可在更大程度上实现执行过程优化。比如,拥有数据的节点正被其他Task占用,是否需要将数据移动到其他空闲节点上呢?不一定,因为,如果预测发现当前节点结束当前任务要比移动数据的时间还要少,那么调度就会等待,知道当前节点可用。

3-4 RDD的设计与运行原理

Spark的核心是建立在统一的抽象RDD上,使Spark的各个组件无缝进行集成,在同一个应用程序中完成大数据计算任务。不同的RDD间的转换操作形成依赖关系,可实现管道化,避免中间结果的存储,降低数据复制、磁盘IO和序列化开销。

3-4-1 RDD概念

一个RDD是一个分布式对象集合,本质上是一个只读的分区记录集合。每个RDD可分成多个分区,每个分区是一个数据集片段,不同分区可保存到集群中不同的节点上,从而在集群的不同节点上并行计算。RDD是高度受限的共享内存模型,即RDD是只读的记录分区的集合,不能直接修改,只能基于稳定的物理存储中的数据集创建RDD,或者通过在其他RDD上执行确定的转换操作(如map、join、group by)而创建新的RDD。

RDD提供了丰富的操作以及支持常见的数据运算,分为“动作”(Action)和“转换”(Transformation),动作(count、collect等)用于执行计算并指定输出的形式,接受RDD,但是返回非RDD,而输出一个值或结果;转换(map、filter、groupBy、join)指定RDD间的相互依赖关系,接受RDD并返回RDD。RDD提供的API都是类似于map、filter、groupBy、join的粗粒度数据转换操作,不是针对某个数据项的细粒度修改。因此,RDD适合于对数据集中元素执行相同操作的批处理应用,而不适于需要异步、细粒度转换的应用。

表 常用的几个Action API

Action API 说明
count() 返回数据集中的元素个数
collect() 以数组的形式返回数据集中的所有元素
first() 返回数据集中的第一个元素
take(n) 以数组形式返回数据集中的前n个元素
reduce(func) 通过函数func(输入两个参数并返回一个值)聚合数据集中的元素
foreach(func) 将数据集中的每个元素传到函数func中运行

表 常用的几个常用的Transformation API

Transformation API 说明
filter() 筛选出满足函数func的元素,并返回一个新的数据集
map(func) 将每个元素传递到函数func中,并将结果返回为一个新的数据集
flatMap(func) 与map类似,但每个输入元素都可以映射到0或多个输出结果
groupByKey() 应用于(K,V)键值对的数据集时,返回一个新的(K,Iterable<V\>)形式的数据集
reduceByKey() 应用于(K,V)键值对的数据集时,返回一个新的(K,V)形式的数据集,其中的每个值是将每个key传递到函数func中进行聚合


RDD典型的执行过程如下:

  • RDD读入外部数据源进行创建;
  • RDD经过一系列的“转换”操作,每次都会产生不同的RDD,供下一次转换使用;
  • 最后一个RDD经“动作”操作进行处理,并输出到外部数据源。

RDD采用惰性调用,即在RDD执行过程中,真正的计算发生在RDD的动作操作,对于之前的所有转换操作,Spark只记录RDD生成的轨迹,即相互间的依赖关系,而不会触发真正的计算。进行动作操作的时候,Spark会根据RDD的依赖关系生成DAG,并从起点开始真正的计算。Spark根据RDD的依赖关系生成DAG,其中的逻辑处理成为一个Lineage(血缘关系),即DAG拓扑排序的结果。通过血缘关系连起来的RDD操作可实现管道化(pipeline),避免多次转换操作之间的数据同步等待,不会产生过多的中间数据,管道化后,一个操作的结果直接管道式地流入下一个操作。管道化也保证了每个操作在处理逻辑上的单一性,不像MR那样,为减少MR过程,在单个MR中写入复杂的逻辑。

3-4-2 RDD特性

Spark采用RDD实现高效计算的原因主要如下:

  • 高效的容错性。现有的分布式共享内存,KV存储,内存数据库等,为实现容错,须在集群节点之间进行数据复制或记录日志,这样节点间会有大量的数据传输。对于数据密集型应用而言会带来很大的开销。而在RDD的设计中,数据只读,不可修改。若需修改数据,须从父RDD转换到子RDD,在不同的RDD间建立血缘关系。所以,RDD天生具有容错机制,不需通过数据冗余的方式(如检查点)实现容错,只需通过RDD父子依赖(血缘)关系重新计算得到丢失的分区来实现容错,无需回归整个系统,避免数据复制的高开销,且重算可在不同节点间并行进行,实现高效容错。RDD依赖关系只需记录粗粒度转换操作,不需记录具体的数据和各种细粒度操作日志,降低了数据密集型应用的容错开销。
  • 中间结果持久化到内存。数据在内存中多个RDD操作之间进行传递,不需“落地”到磁盘,避免不必要的磁盘IO开销。
  • 存放的数据可是Java对象,避免不必要的对象序列化和反序列化。

3-4-4 RDD间的依赖关系

RDD中的依赖关系分为窄依赖(Narrow Dependency)与宽依赖(Wide Dependency)。


RDD宽依赖、窄依赖
图 RDD宽依赖、窄依赖的区别

如果父RDD的一个分区只被一个子RDD的一个分区所使用就是窄依赖(map、filter、union),否则就是宽依赖(groupByKey、sortByKey),宽依赖常伴随着Shuffle操作。对于连接(join)操作,若连接操作使用的每个分区仅仅和已知的分区进行连接,是窄依赖,如上图(a)中的连接操作,其他的情况下的连接操作是宽依赖,如上图(b)中连接操作。

宽窄依赖关系,使Spark具有天生的容错性,加快了Spark的执行速度。当RDD部分分区数据丢失时,可通过血缘关系获取足够的信息来重新运算和恢复丢失的数据分区,从而带来性能提升。窄依赖的失败更为高效,它只需根据父RDD分区重新计算丢失的分区即可,且可在不同的节点并行计算。

3-4-5 Stage划分

Spark根据RDD的依赖关系生成DAG,在分析RDD中分区之间的依赖关系划分Stage。划分方法:在DAG中进行反向解析,遇到宽依赖就断开,遇到窄依赖就把当前的RDD加入到Stage中;将窄依赖尽量划分到同一个Stage中,可实现流水线计算。


根据RDD分区的依赖关系划分Stage
图 根据RDD分区的依赖关系划分Stage

上图,从HDFS中读入数据生成3个不同RDD(A、C、E),通过一系列转换后将结果保存到HDFS中。对DAG解析时,在依赖图中从右向左进行反向解析,可得到三个Stage。在Stage2中,从map到union都是窄依赖,这两步可形成一个流水线操作,比如,分区7通过map操作生成的分区9,可不用等待分区8到分区10这个转换操作计算结束,就可以继续进行union操作,得到分区13。这样的流水线执行大大提高了计算的效率。

Stage的类型包括两种:ShuffleMapStage和ResultStage。

  • ShuffleMapStage:不是最终的Stage,它之后还有其他Stage。它的输出需要经过Shuffle过程,作为后续Stage的输入;此Stage以Shuffle为输出边界,起输入边界可从外部获取数据,也可是另一个ShuffleMapStage的输出,其输出可是另一个Stage的开始。一个Job里可能有,也可能没有该类型的Stage;
  • ResultStage:最终的Stage,没有输出,而是直接产生结果或存储。其输入边界可是从外部获取数据,也可是另一个ShuffleMapStage的输出。一个Job中必定有该类型的Stage。

3-4-5 运行过程

  • 创建RDD对象;
  • SparkContext负责计算RDD之间的依赖关系,构建DAG;
  • DAGScheduler负责将DAG图分解成多个Stage,每个Stage中含多个Task,每个Task会被TaskScheduler分发给各个WorkNode上的Executor去执行。

    RDD在Spark中的运行过程
    图 RDD在Spark中的运行过程

四、Spark SQL

Spark SQL前身是Shark,Shark是Spark上的数据仓库,与Hive兼容。2014年停止开发,Spark SQL全面继承Shark,并优化。

4-1 Shark

Shark即Hive on Spark,为了实现与Hive兼容,在HiveQL方面重用了Hive的HiveQL解析、逻辑执行计划翻译、执行计划优化等逻辑,可近似认为仅将物理执行从MR作业转换成Spark左右。

导致的问题:

  • 执行计划完全依赖于Hive,不方便添加新的优化策略;
  • 因为Spark是线程级并行,而MR是进程级并行,Spark在兼容Hive的实现上存在线程安全问题,导致Shark不得不使用另一套独立维护的打了补丁的Hive源码分支。

4-2 Spark SQL设计

Spark SQL在Shark架构上重写了逻辑执行计划的优化部分,解决了其存在问题。Spark SQL在Hive兼容层面仅依赖HiveQL解析和Hive元数据,从HQL被解析成抽象语法树(AST)起,就全部由Spark SQL接管了。Spark SQL执行计划生成和优化都由Catalyst(函数式关系查询优化框架)负责。


Spark SQL架构
图 Spark SQL架构

Spark SQL增加SchemaRDD(即带有Schema信息的RDD),使得可执行SQL语句。Spark SQL的数据即可来自RDD,也可来自Hive、HDFS、Cassandra等外部数据源,还可以是Json格式的数据。Spark SQL支持Scala、Java、Python语言。

五、Spark Streaming

Spark Streaming是构建在Spark上的实时计算框架,扩展了Spark处理大规模流式数据的能力,是Spark的核心组件之一,为Spark提供了可拓展、高吞吐、容错的流计算能力。它可整合多种输入数据源,如Kafka、Flume、HDFS、TCP Socket,经处理后的数据可存储到HDFS文件系统、数据库、或显示在仪表盘(Dashboards)上。

Spark Streaming中最主要的抽象是DStream(Discretized Stream,离散化数据流),表示连续不断的数据流。其基本原理是将实时输入数据流以时间片(秒级)为单位拆分成一段段的DStream,然后经Spark引擎以类似批处理的方式处理每个时间片数据,即每段数据转换成RDD,将对DStream的操作转换成对RDD的操作。

Spark Streaming与Storm最大的区别是:

  • 它无法实现毫秒级流计算,而Storm可以。
  • 前者将数据流按batch size(通常0.5~2秒之间)分解成一系列的批处理作业,而Storm的处理单位是Tuple,只需极小的延迟。
  • 相对于Storm,Streaming的RDD操作更容易做高效的容错处理
  • Streaming采用小批量处理的方式,使得它兼容批量和实时数据处理的逻辑和算法,方便了一些需要历史数据和实时数据联合分析的特定应用场合。

六、Spark的部署和应用方式

  • Standalone模式,采用Spark自带的资源调度管理服务,独立部署到集群中。
  • Spark on Mesos模式,在Mesos资源调度管理框架上,运行Spark服务。Mesos与Spark有血缘关系,Spark在设计开发时,充分考虑对Mesos的支持,相比运行在YARN上更加灵活,自然。Spark官方推荐。
  • Spark on YARN模式,运行在YARN框架上,与Hadoop统一部署。资源调度和管理依赖于YARN,分布式存储则依赖于HDFS。

Hadoop和Spark的统一部署

Spark还无法实现Hadoop生态系统中某些组件所实现的功能,比如Storm可实现毫秒级应用。另外,企业中已有的应用很多都基于Hadoop组件开发,完全转移到Spark上需要一定的成本。因此,统一部署是合理的选择。Hadoop MR、HBase、Storm、Spark都运行在YARN上,可在其上统一部署,带来如下好处:

  • 计算资源按需伸缩;
  • 不用负载应用混搭,集群利用率高;
  • 共享底层存储,避免数据跨集群迁移。

【参考】http://dblab.xmu.edu.cn/wp-content/uploads/2016/01/%E5%8E%A6%E9%97%A8%E5%A4%A7%E5%AD%A6%E6%9E%97%E5%AD%90%E9%9B%A8%E7%BC%96%E8%91%97-%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%8A%80%E6%9C%AF%E5%8E%9F%E7%90%86%E4%B8%8E%E5%BA%94%E7%94%A8-%E7%94%B5%E5%AD%90%E4%B9%A6-%E7%AC%AC16%E7%AB%A0-Spark%EF%BC%882016%E5%B9%B44%E6%9C%8820%E6%97%A5%E7%89%88%E6%9C%AC%EF%BC%89.pdf

猜你喜欢

转载自blog.csdn.net/namelessml/article/details/52583727