1.Spark简介
1.1定义
是一个数据计算框架,可以实现离线批处理,交互式查询,实时流计算,机器学习,图计算。
1.2特点
- 基于内存计算,所以速度会比MR快很多。MR慢的原因之一就是过程中有频繁的磁盘I/O读写,尤其shuffle过程速度更慢。
- 有大量的RDD算子可以用,开发比较简单直接调API.
1.3Spark与MR
同样都是计算框架,但是因为后者基于内存计算,shuffle过程比后者简单,所以速度更快。
spark的劣势如果一次性针对特别大的数据,比如10E级别,MR非常稳定,即使慢慢算,也会出结果,不会报错。而spark则会内存溢出,或者程序都跑不起来等情况。
1.4Spark与Storm
虽然 Spark stream和storm都可以进行实时计算,但是计算方式完全不同。storm是真正意义上的实时计算,来一条计算一条,而spark更像伪事实。比如一个RDD处理一秒内的数据,等一秒内的数据都包装起来了,再对这个RDD进行操作。
与此同时,再分布式流处理时。storm可以实时调整并行度,而spark不可。
spark的优点也有很多,比如spark是基于一个batch处理的。比一条一条处理的storm吞吐量大得多。其次,spark stream可以无缝和spark其他组件比如Mlib和Greaph。SparkCore等无缝配合。
1.5Spark与Hive
Hive作为离线数仓,除了提供查询功能还提供存储功能。而spark没有存储模块。spark应该是替代hive的查询引擎。因为hive引擎用的MR,所以速度会很慢。
2.Spark原理
2.1SparkCore
- Spark基础配置
- SparkConf配置信息
- SparkContext上下文
- SparkRPC过程调用
- ListenserBus事件总线
- MetricxSystem度量系统
- SparkEnv环境变量
- 存储系统
- 内存
- 磁盘
- 计算层
- 内存管理
- 任务管理
- Task管理
- Shuffle管理
- 调度原理
- DAG调度
- Task调度
2.1.0概述
每个spark程序包含一个Driver程序其中有main函数,然后在集群上并行执行main中的业务代码。
2.1.1Spark基础配置
sparkContext是spark应用程序入口,正常开发调用spark api即可
2.1.2Spark存储系统
优先把数据缓存在内存中,内存不足时写入磁盘。可以远程调用将结果输出到hdfs或者hbase中。
2.1.3Spark调度系统
- DAGScheduiler
- 每一个job根据RDD的关系分为多个Stage
- Stage抽象为Task交给
- TaskScheduler进行任务调度
- 主要算法有FIFO和FAIR
- FIFO:先进先出。这是默认的模式
- FAIR:作业分组到池,每个池分配不同的权重,任务按权重决定执行顺序。
2.2SparkSQL
Spark支持两种数据抽象
DataFrame
对结构数据的抽象,可以理解为spark的表。
DataSet
数据的分布式集合,由Jvm构建。可以使用map。flatmap等操作函数操作。
RDD
分布式对象集合,提升数据的执行效率。是弹性分布式数据集,他是对数据的映射。RDD会被分区,然后分到多个分区。每个分区分布在集群不同的节点,从而可以让RDD中数据并行操作。在内存不足时,RDD也会被写到节点的磁盘里。该RDD由所分节点处理完后,还可以去第二批节点,继续处理。可以去多批节点,这就是内存上的迭代。如果在该批次某一台节点丢失了数据,Spark会重新根据上一个节点的RDD计算得到新节点的数据。
RDD主要有两种操作,Transformating和Action两种操作。
- Transformation(转换)是在一次迭代中切换成新的RDD,不会立即出结果,有map。filter。flatmap等操作。
- Action(执行)对已有的RDD计算产生结果,将结果返回Driver或者写入外部,有reduce,SavaAsTextfile等方法。
2.3SparkStreaming
主要是支持对流数据的处理,支持数据的可伸缩和容错处理。
2.5 RDD
rdd是弹性分布式数据集具体可以看SparkRDD.md
2.6 Spark-Shell
shell使用时会自动生成一个sparkcontext (sc)
2.7 Shuffle
某些RDD会调起shuffle操作,其作用是将数据重新分布。结果数据会子啊各个分区重新分组,shuffle通常开销会很大,因为涉及到跨excutor和跨节点复制数据。
以reducebykey为例,首先需要把其他分区中相同key的元素转移到当前分区,方便后续reduce操作。
shuffle后元素顺序不是确定的。需要使用.sort排序每个分区
repartitionAndSortWithinPartitions
重分区的同时,对分区进行排序,比自行组合repartition和sort更高效
sortBy 创建一个全局有序的RDD
会导致混洗的算子有:
**重分区(repartition)**类算子,如: repartition 和 coalesce;
ByKey 类算子(除了计数类的,如 countByKey) 如:groupByKey和 reduceByKey;
Join类算子,如:cogroup和 join.
性能影响
混洗合计大量磁盘I/O,网络I/O.还有序列化操作。
单个任务是内存中执行的,直到放不下溢出时放磁盘,在reduce时会基于目标分区重新排序,并写到一个文件里。reduce到时只读取与任务相关的blocks。
因为一个任务通常有多个stage。为了防止数据丢失时重新shuffle运算,通常全部数据只有运算结束时才会清除。
2.8 RDD持久化
首先需要明白 RDD的血缘关系可以重用但是数据不可以,数据每次使用都会重新计算
这里的数据是指 结果数据
rdd1=map(xxxx)
rdd2=rdd1.flatmap
如图,首先,rdd不存储数据。所以rdd2调用rdd1时,rdd1的数据会重新计算在给rdd2。这就是数据不可复用但血缘可复用。相当于rdd1把自己的计算过程给rdd2,让其重新计算。
所以缓存和持久化保存数据后,rdd2再调用,就可以直接获取数据。
数据 结果 可以持久化或者缓存到内存中,当你持久化了一个RDD,所有的节点都会存储该RDD的一些 分区
后续可以复用,通常会提速10倍。
你可以用persist() 或者 cache() 来标记一下需要持久化的RDD。等到该RDD首次被施加action算子的时候,其对应的数据分区就会被保留在内存里。同时,Spark的缓存具备一定的容错性 – 如果RDD的任何一个分区丢失了,Spark将自动根据其原来的血统信息重新计算这个分区。
另外,每个持久化的RDD可以使用不同的存储级别,比如,你可以把RDD保存在磁盘上,或者以java序列化对象保存到内存里(为了省空间),或者跨节点多副本,或者使用 Tachyon 存到虚拟机以外的内存里。这些存储级别都可以由persist()的参数StorageLevel对象来控制。cache() 方法本身就是一个使用默认存储级别做持久化的快捷方式,默认存储级别是 StorageLevel.MEMORY_ONLY(以java序列化方式存到内存里)。完整的存储级别列表如下:
存储级别 | 含义 |
---|---|
MEMORY_ONLY | 以未序列化的Java对象形式将RDD存储在JVM内存中。如果RDD不能全部装进内存,那么将一部分分区缓存,而另一部分分区将每次用到时重新计算。这个是Spark的RDD的默认存储级别。 |
MEMORY_AND_DISK | 以未序列化的Java对象形式存储RDD在JVM中。如果RDD不能全部装进内存,则将不能装进内存的分区放到磁盘上,然后每次用到的时候从磁盘上读取。 |
MEMORY_ONLY_SER | 以序列化形式存储RDD(每个分区一个字节数组)。通常这种方式比未序列化存储方式要更省空间,尤其是如果你选用了一个比较好的序列化协议(fast serializer),但是这种方式也相应的会消耗更多的CPU来读取数据。 |
MEMORY_AND_DISK_SER | 和MEMORY_ONLY_SER类似,只是当内存装不下的时候,会将分区的数据吐到磁盘上,而不是每次用到都重新计算。 |
DISK_ONLY | RDD数据只存储于磁盘上。 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. | 和上面没有”_2″的级别相对应,只不过每个分区数据会在两个节点上保存两份副本。 |
OFF_HEAP (实验性的) | 将RDD以序列化格式保存到Tachyon。与MEMORY_ONLY_SER相比,OFF_HEAP减少了垃圾回收开销,并且使执行器(executor)进程更小且可以共用同一个内存池,这一特性在需要大量消耗内存和多Spark应用并发的场景下比较吸引人。而且,因为RDD存储于Tachyon中,所以一个执行器挂了并不会导致数据缓存的丢失。这种模式下Tachyon 的内存是可丢弃的。因此,Tachyon并不会重建一个它逐出内存的block。如果你打算用Tachyon做为堆外存储,Spark和Tachyon具有开箱即用的兼容性。 |
Spark会自动持久化一些混洗(shuffle)操作(如:reduceByKey)的中间数据,即便用户根本没有调用persist。这么做是为了避免一旦有一个节点在混洗过程中失败,就要重算整个输入数据。当然,我们还是建议对需要重复使用的RDD调用其persist算子。
如何选择存储级别?
Spark的存储级别主要可于在内存使用和CPU占用之间做一些权衡。建议根据以下步骤来选择一个合适的存储级别:
如果RDD能使用默认存储级别(MEMORY_ONLY),那就尽量使用默认级别。这是CPU效率最高的方式,所有RDD算子都能以最快的速度运行。
如果步骤1的答案是否(不适用默认级别),那么可以尝试MEMORY_ONLY_SER级别,并选择一个高效的序列化协议(selecting a fast serialization library),这回大大节省数据对象的存储空间,同时速度也还不错。
尽量不要把数据吐到磁盘上,除非:1.你的数据集重新计算的代价很大;2.你的数据集是从一个很大的数据源中过滤得到的结果。否则的话,重算一个分区的速度很可能和从磁盘上读取差不多。
如果需要支持容错,可以考虑使用带副本的存储级别(例如:用Spark来服务web请求)。所有的储级别都能够以重算丢失数据的方式来提供容错性,但是带副本的存储级别可以让你的应用持续的运行,而不必等待重算丢失的分区。
在一些需要大量内存或者并行多个应用的场景下,实验性的OFF_HEAP会有以下几个优势:
Spark能够自动监控各个节点上缓存使用率,并且以LRU(最近经常使用)的方式将老数据逐出内存。如果你更喜欢手动控制的话,可以用RDD.unpersist() 方法来删除无用的缓存。
3.Spark运行模式和集群
3.0集群运行模式
local
本地模式,分为local单线程和local-ckuster多线程模式
standalone
集群模式,独立模式,spark用自己的资源调度框架运行。时master/slave模式
yarn
集群模式,有Yarn负责资源管理。spark负责任务调度和计算。
Mesos
集群模式,mesos负责资源管理,spark负责任务调度和计算。
K8s
集群模式,在k8s上运行
3.1集群角色
ClusterManger
集群管理器,在Master进程里。对应用程序申请资源的管理。有local,standalone,yarn。mesos等模式。
Worker
用于执行任务的提交
- worker节点用注册机像clusterManger汇报executor状态,cpu内存等。
- 创建executor。这是计算单元。
- 在executor上执行master分配的task。
yarn模式下,work节点是指NodeManger节点。
executor
执行计算任务的组件,负责Task的运行,可以把数据存储在内存或磁盘中。也能将数据返回Driver。
application
是SparkAPI编写的程序,编写在各个executor上执行的代码。
Driver
负责将job转为tasks,在各个exexutor中协调调度task。
SparkContext
spark所有功能的入口,初始化spark所需要的所有组件。负责向master注册等。
3.2调度
DAGScaeduler
面向stage的调度器,负责把DAG拆分为Task,每组Task是stage。
TaskScheduler
负责具体任务的执行,任务调度管理。状态结果跟踪,任务执行,取得结果。
job
多个stage构成,有action操作来触发。一个job包含多个RDD以及作用在RDD上的各种操作算子。
stage
DAGScheduler会把DAG分割为依赖的Stage,执行时,chid stage会等待parent stage执行完毕。然后进入下一个阶段。在stage内部,操作时popeline串行执行的。
4.运行流程
- sparkContext向ClusterManger申请资源
- clusterManger分配资源,在worker节点创建executor
- sparkContext发送程序代码和task任务给executor,等结果出来了sparkContext收集结果给driver