大数据技术概述复习(二)Spark原理简单介绍

大数据技术概述复习(二)Spark原理简单介绍

本文整理复习自用,仅供参考

引用:

1《大数据技术原理与应用(第3版)》

2 HDFS和MapReduce优缺点 https://www.cnblogs.com/shaosks/p/9440848.html

3 Spark RDD的蛮荒世界-https://blog.csdn.net/sjh752422969/article/details/55122736

4 Kidron - https://www.zhihu.com/question/34244290/answer/94630001

1.Mapreduce引擎的缺陷

MapReduce主要三点缺陷:

  1. 表达能力有限。计算必须转化成Map和Reduce的操作,不够通用,难以描述复杂的数据处理过程。实际开发时需要编写不少相对底层的代码,效率低、不方便编写。
  2. 磁盘IO开销大。每次执行都需要从磁盘 中读取数据,计算完成后的中间结果也要写入磁盘。进行迭代运算时非常耗资源。
  3. 计算延迟高。一次计算中,任务之间的衔接存在延迟(涉及IO操作),而且后续任务必须等待前一个任务完成。难以应对复杂、多阶段的计算任务。

以上缺陷导致Mapreduce无法胜任实时、快速计算的需求,只适用于离线批处理的应用场景。

MapReduce在下面这些场景下,处理的效果差:

1、实时计算:MapReduce无法在毫米或秒级内返回结果。

​ 2、流计算:流计算的输入数据是动态的,而MapReduce的输入数据是静态的,不能动态变化,这是因为MapReduce自身的设计特点决定了数据源必须是静态的。

3、DGA(有向图)计算:多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入磁盘,会造成大量的词频IO

2.新一代引擎Spark的出现

使用MapReduce无法满足计算需求,故有了spark的出现。Spark是借鉴了MapReduce的思想并在其基础上发展起来的,继承了其分布式计算的优点并改进其缺陷

2.1Spark与Mapreduce 的比较
  1. 程序运行更快。Spark提供了内存计算,中间结果写入内存中,提高了迭代运算效率。

    mapreduce的之间结果需要落地,保存到磁盘,会产生大量IO操作,影响性能。

  2. 使计算引擎更加通用。Spark的计算模型也属于MapReduce,但同时还提供了多种数据集操作类型,编程模型更加灵活。

    spark更加通用,提高了丰富的算子(如Transformation和Action),还有流计算Streaming和图计算GraphX等;而mapreduce只有map和reduce两种操作,并不是所有的问题都可以简单地分成map和reduce两步模型来处理。

  3. 有更优的任务调度机制。Spark采用了基于DAG的任务调度执行机制,优于MapReduce的迭代执行机制。

  4. 使编写程序更加容易。提供了多种高层次、简介的API,大大减少了代码量;而且提供了实时交互式编程反馈(spark-shell),方便验证调试。

总结

  • spark生态更为丰富,功能更强大,性能更佳,使用范围广;
  • mapreduce更简单,稳定性好,适合长期后台运行,适合离线海量数据(挖掘)处理。

3Spark生态系统

在实际应用中,大数据处理主要包括以下三个类型:

  • 复杂的批量数据处理:时间跨度通常在数十分钟到数小时之间;
  • 基于历史数据的交互式查询:时间跨度通常在数十秒到数分钟之间;
  • 基于实时数据流的数据处理:时间跨度通常在数百毫秒到数秒之间。

Spark的设计遵循“一个软件栈满足不同应用场景”的理念,逐渐形成了一套完整的生态系统。既能够提供内存计算框架,也可以支持SQL即席查询、实时流式计算、机器学习和图计算等。

Spark可以部署在资源管理器YARN之上,提供一站式的大数据解决方案。因此,Spark所提供的生态系统足以应对上述三种场景,即同时支持批处理、交互式查询和流数据处理。使用Spark,免除了同时部署多种软件带来的麻烦。

3.1BDAS伯克利数据分析栈

目前,Spark已经发展成为包含众多子项目的大数据计算平台。

伯克利将Spark的整个生态系统称为伯克利数据分析栈(BDAS)。

  • 其核心框架是Spark,

  • 同时BDAS涵盖支持结构化数据SQL查询与分析的查询引擎Spark SQL,

  • 提供机器学习功能的系统MLbase及底层的分布式机器学习库MLlib、

  • 并行图计算框架GraphX、

  • 流计算框架Spark Streaming、

  • 采样近似计算查询引擎BlinkDB、内存分布式文件系统Tachyon、资源管理框架Mesos等子项目。

这些子项目在Spark上层提供了更高层、更丰富的计算范式。

BDAS架构图

从中可以看出,Spark专注于数据的处理分析,而数据的存储还是要借助于Hadoop分布式文件系统HDFS、Amazon S3等来实现的。因此,Spark生态系统可以很好地实现与Hadoop生态系统的兼容,使得现有Hadoop应用程序可以非常容易地迁移到Spark系统中。

各个组件的具体功能如下:

  • Spark Core:Spark Core包含Spark的基本功能,如内存计算、任务调度、部署模式、故障恢复、存储管理等。Spark建立在统一的抽象RDD之上,使其可以以基本一致的方式应对不同的大数据处理场景;通常所说的Apache Spark,就是指Spark Core;

  • Spark SQL:Spark SQL允许开发人员直接处理RDD,同时也可查询Hive、HBase等外部数据源。Spark SQL的一个重要特点是其能够统一处理关系表和RDD,使得开发人员可以轻松地使用SQL命令进行查询,并进行更复杂的数据分析;

  • Spark Streaming:Spark Streaming支持高吞吐量、可容错处理的实时流数据处理,其核心思路是将流式计算分解成一系列短小的批处理作业。Spark Streaming支持多种数据输入源,如Kafka、Flume和TCP套接字等;

  • MLlib(机器学习):MLlib提供了常用机器学习算法的实现,包括聚类、分类、回归、协同过滤等,降低了机器学习的门槛,开发人员只要具备一定的理论知识就能进行机器学习的工作;

  • GraphX(图计算):GraphX是Spark中用于图计算的API,可认为是Pregel在Spark上的重写及优化,Graphx性能良好,拥有丰富的功能和运算符,能在海量数据上自如地运行复杂的图算法。

4.Spark架构

4.1spark运行架构

Spark运行架构包括:

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

图9-5 Spark运行架构

在Spark中,一个应用(Application)由一个任务控制节点(Driver)和若干个作业(Job)构成,一个作业由多个阶段(Stage)构成,一个阶段由多个任务(Task)组成。

当执行一个应用时,任务控制节点会向集群管理器(Cluster Manager)申请资源,启动Executor,并向Executor发送应用程序代码和文件,然后在Executor上执行任务,运行结束后,执行结果会返回给任务控制节点,或者写到HDFS或者其他数据库中。

4.1.1Spark运行基本流程

图9-7 Spark运行基本流程图

(1)当一个Spark应用被提交时,首先需要为这个应用构建起基本的运行环境,即由任务控制节点(Driver)创建一个SparkContext。由SparkContext负责和资源管理器(Cluster Manager)的通信以及进行资源的申请、任务的分配和监控等。

(2)资源管理器为Executor分配资源,并启动Executor进程。Executor运行情况将随着“心跳”发送到资源管理器上。

(3)SparkContext构建DAG图

(4)DAG调度器(DAGScheduler)将DAG图分解成多个“阶段”,然后把一个个“任务集”提交给底层的任务调度器(TaskScheduler)进行处理。

(5)Executor向SparkContext申请任务,任务调度器将任务分发给Executor运行,同时,SparkContext将应用程序代码发放给Executor。

(6)任务在Executor上运行,运行完毕后写入数据并释放所有资源。

4.2 Spark核心-RDD

Spark的核心是建立在统一的抽象RDD之上,使得Spark的各个组件可以无缝进行集成,在同一个应用程序中完成大数据计算任务。RDD为用户屏蔽了底层对数据的复杂抽象和处理,为用户提供了一组方便的数据转换与求值方法。

4.2.1RDD的引入

在实际应用中,存在许多迭代式算法(比如机器学习、图算法等)和交互式数据挖掘工具,这些应用场景的共同之处是,不同计算阶段之间会重用中间结果,即一个阶段的输出结果会作为下一个阶段的输入。目前的框架(MapReduce和Dryad)缺少利用分布式内存的抽象能力,这使得重用中间结果的操作效率低下,要多次读写到磁盘中,性能普遍不佳。

RDD就是为了解决这一问题而出现的。

4.2.2RDD基本概念

弹性分布式数据集(RDD,Resilient Distributed Datasets)提供了一种高度受限的共享内存模型,即RDD是只读的、分区记录的集合,只能通过在稳定的存储器或其他RDD的数据上的确定性操作来创建,这些限制使得实现容错的开销很低。

浅显解释:
“RDD,包含一组分区列表(实际上是分区到block的映射,具体数据可以是分布式的存储在HDFS各个节点上)以及一组transformation或action算子等,前者用于执行计算并指定输出的形式,后者指定RDD之间的相互依赖关系。”

对弹性分布式数据集的理解:

(1)数据集:RDD本质上是只读的、分区记录的集合

(2)弹性:《Learning Spark:Lightning-fast Data Analysis》一书中解释“弹性”是指在任何时候都能进行重算。这样当集群中的一台机器挂掉而导致存储在其上的RDD丢失后,Spark还可以重新计算出这部分的分区的数据。但用户感觉不到这部分的内容丢失过。这样RDD数据集就像块带有弹性的海绵一样,不管怎样挤压(分区遭到破坏)都是完整的。“弹性”也指计算过程中内存不够时它会和磁盘进行数据交换。

(3)分布式:一个数据集分别放在几个机器上,而RDD只要存储这些数据的元信息(如那一片在哪个机器上)即可。每个RDD可以分成多个分区,每个分区就是一个数据集片段,并且一个RDD的不同分区可以被保存到集群中不同的节点上,从而可以在集群中的不同节点上进行并行计算。

RDD特性:

  • 高效容错:RDD通过基于数据集粗粒度转换、线性血脉关系的管理,很好地解决了容错性问题,通过依赖关系还原现场重建数据块,而不是直接复制数据。

“粒度似乎是根据项目模块划分的细致程度区分的,一个项目模块(或子模块)分得越多,每个模块(或子模块)越小,负责的工作越细,就说粒度越细,否则为粗粒度”。

RDD提供的转换接口都非常简单,都是类似map、filter、groupBy、join等粗粒度的数据转换操作,而不是针对某个数据项的细粒度修改。因此,RDD比较适合对于数据集中元素执行相同操作的批处理式应用,而不适合用于需要异步、细粒度状态的应用,比如Web应用系统、增量式的网页爬虫等。

  • 中间结果持久化到内存:数据在内存中的多个RDD操作之间进行传递,不需要“落地”到磁盘上,避免了不必要的读写磁盘开销;

  • 存放的数据可以是Java对象,避免了不必要的对象序列化和反序列化开销。

RDD的五大属性:

  • 一组分片(A list of partitions),即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。

  • 一个计算每个分区的函数(A function for computing each split)。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。

  • 一个RDD之间的依赖关系表(A list of dependencies on other RDDs)。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。

  • 可选属性,RDD的分片函数(Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned))。对于kv类型的rdd才会有分区函数(必须要产生shuffle),对于不是kv类型的rdd分区函数是None。 分区函数决定了原始rdd的数据会流入到下面rdd的哪些分区中。

  • 可选属性,一组最优的数据块的位置(Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file))。这里涉及到数据的本地性和数据位置最优 spark后期在进行任务调度的时候,会优先考虑存有数据的worker节点来进行任务的计算。大大减少数据的网络传输,提升性能。

RDD的两类方法:转换(Transformations)和操作(Actions):

常用RDD操作整理:https://www.cnblogs.com/jiangzhengjun/p/7262530.html

4.2.3RDD运行原理

RDD典型的执行过程

Spark用Scala语言实现了RDD的API,程序员可以通过调用API实现对RDD的各种操作。RDD典型的执行过程如下:

  1. RDD读入外部数据源(或者内存中的集合)进行创建;

  2. RDD经过一系列的“转换”操作,每一次都会产生不同的RDD,供给下一个“转换”使用;

  3. 最后一个RDD经“行动”操作进行处理,并输出到外部数据源(或者变成Scala集合或标量)。

图9-9  RDD执行过程的一个实例

  • 在上图中,从输入中逻辑上生成A和C两个RDD,经过一系列“转换”操作,逻辑上生成了F(也是一个RDD),之所以说是逻辑上,是因为这时候计算并没有发生,Spark只是记录了RDD之间的生成和依赖关系。当F要进行输出时,也就是当F进行“行动”操作的时候,Spark才会根据RDD的依赖关系生成DAG,并从起点开始真正的计算。

这一系列处理称为一个“血缘关系(Lineage)”,即DAG拓扑排序的结果。

  • RDD采用了惰性调用,即在RDD的执行过程中,真正的计算发生在RDD的“行动”操作,对于“行动”之前的所有“转换”操作,Spark只是记录下“转换”操作应用的一些基础数据集以及RDD生成的轨迹,即相互之间的依赖关系,而不会触发真正的计算。

采用惰性调用,通过血缘关系连接起来的一系列RDD操作就可以实现管道化(pipeline),避免了多次转换操作之间数据同步的等待,而且不用担心有过多的中间数据,因为这些具有血缘关系的操作都管道化了,一个操作得到的结果不需要保存为中间数据,而是直接管道式地流入到下一个操作进行处理。同时,这种通过血缘关系把一系列操作进行管道化连接的设计方式,也使得管道中每次操作的计算变得相对简单,保证了每个操作在处理逻辑上的单一性。

例:一个Spark的“Hello World”程序

这里以一个“Hello World”入门级Spark程序来解释RDD执行过程,这个程序的功能是读取一个HDFS文件,计算出包含字符串“Hello World”的行数。

//创建SparkContext对象
val sc= new SparkContext(“spark://localhost:7077,”Hello World”, “YOUR_SPARK_HOME”,”YOUR_APP_JAR”) 
                         
//从HDFS文件中读取数据创建一个RDD
val fileRDD = sc.textFile(“hdfs://192.168.0.103:9000/examplefile”)
                          
//对fileRDD进行转换操作得到一个新的RDD,即filterRDD;
val filterRDD = fileRDD.filter(_.contains(“Hello World”))
                          
//对filterRDD进行持久化,把它保存在内存或磁盘中(这里采用cache接口把数据集保存在内存中),方便后续重复使用
//当数据被反复访问时(比如查询一些热点数据,或者运行迭代算法),这是非常有用的,而且通过cache()可以缓存非常大的数据集,支持跨越几十甚至上百个节点;
filterRDD.cache()
                          
//count()是一个行动操作,用于计算一个RDD集合中包含的元素个数。
filterRDD.count()

可以看出,一个Spark应用程序,基本是基于RDD的一系列计算操作。

这个程序的执行过程如下:

  • 创建这个Spark程序的执行上下文,即创建SparkContext对象;
  • 从外部数据源(即HDFS文件)中读取数据创建fileRDD对象;
  • 构建起fileRDD和filterRDD之间的依赖关系,形成DAG图,这时候并没有发生真正的计算,只是记录转换的轨迹;
  • 执行到第5行代码时,count()是一个行动类型的操作,触发真正的计算,开始实际执行从fileRDD到filterRDD的转换操作,并把结果持久化到内存中,最后计算出filterRDD中包含的元素个数。
4.2.4RDD之间的依赖关系

RDD中不同的操作会使得不同RDD中的分区会产生不同的依赖。RDD中的依赖关系分为窄依赖(Narrow Dependency)与宽依赖(Wide Dependency)。

  • 窄依赖表现为一个父RDD的分区对应于一个子RDD的分区,或多个父RDD的分区对应于一个子RDD的分区;
  • 宽依赖则表现为存在一个父RDD的一个分区对应一个子RDD的多个分区。

窄依赖典型的操作包括map、filter、union等,宽依赖典型的操作包括groupByKey、sortByKey等。对于连接(join)操作,可以分为两种情况。

图9-10 窄依赖与宽依赖的区别

Spark的这种依赖关系设计,使其具有了天生的容错性,大大加快了Spark的执行速度。因为,RDD数据集通过“血缘关系”记住了它是如何从其它RDD中演变过来的,血缘关系记录的是粗颗粒度的转换操作行为,当这个RDD的部分分区数据丢失时,它可以通过血缘关系获取足够的信息来重新运算和恢复丢失的数据分区,由此带来了性能的提升。

  • 相对而言,在两种依赖关系中,窄依赖的失败恢复更为高效,它只需要根据父RDD分区重新计算丢失的分区即可(不需要重新计算所有分区),而且可以并行地在不同节点进行重新计算。

  • 而对于宽依赖而言,单个节点失效通常意味着重新计算过程会涉及多个父RDD分区,开销较大。

  • 此外,Spark还提供了数据检查点和记录日志,用于持久化中间RDD,从而使得在进行失败恢复时不需要追溯到最开始的阶段。在进行故障恢复时,Spark会对数据检查点开销和重新计算RDD分区的开销进行比较,从而自动选择最优的恢复策略。

4.2.5计算阶段的划分

Spark通过分析各个RDD的依赖关系生成了DAG,再通过分析各个RDD中的分区之间的依赖关系来决定如何划分阶段,

具体划分方法是:在DAG中进行反向解析,遇到宽依赖就断开,遇到窄依赖就把当前的RDD加入到当前的阶段中;将窄依赖尽量划分在同一个阶段中,可以实现流水线计算。

图9-11根据RDD分区的依赖关系划分阶段

假设从HDFS中读入数据生成3个不同的RDD(即A、C和E),通过一系列转换操作后再将计算结果保存回HDFS。

  • 对DAG进行解析时,在依赖图中进行反向解析,由于从RDD A到RDD B的转换以及从RDD B和F到RDD G的转换,都属于宽依赖,因此,在宽依赖处断开后可以得到三个阶段,即阶段1、阶段2和阶段3。

可以看出,在阶段2中,从map到union都是窄依赖,这两步操作可以形成一个流水线操作,比如,分区7通过map操作生成的分区9,可以不用等待分区8到分区9这个转换操作的计算结束,而是继续进行union操作,转换得到分区13,这样流水线执行大大提高了计算的效率。

把一个DAG图划分成多个“阶段”以后,每个阶段都代表了一组关联的、相互之间没有Shuffle依赖关系的任务组成的任务集合。每个任务集合会被提交给任务调度器(TaskScheduler)进行处理,由任务调度器将任务分发给Executor运行。

4.2.6 RDD在Spark架构中的运行过程

(1)创建RDD对象;
(2)SparkContext负责计算RDD之间的依赖关系,构建DAG;
(3)DAGScheduler负责把DAG图分解成多个阶段,每个阶段中包含了多个任务,每个任务会被任务调度器分发给各个工作节点(Worker Node)上的Executor去执行。

代表了一组关联的、相互之间没有Shuffle依赖关系的任务组成的任务集合。每个任务集合会被提交给任务调度器(TaskScheduler)进行处理,由任务调度器将任务分发给Executor运行。

4.2.6 RDD在Spark架构中的运行过程

(1)创建RDD对象;
(2)SparkContext负责计算RDD之间的依赖关系,构建DAG;
(3)DAGScheduler负责把DAG图分解成多个阶段,每个阶段中包含了多个任务,每个任务会被任务调度器分发给各个工作节点(Worker Node)上的Executor去执行。

图9-12 RDD在Spark中的运行过程

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值