Spark分布式计算原理浅谈
一、判断RDD算子种类的方法(对上一篇的补充):
1.变换操作:
- 延迟执行;
- 返回新的Rdd。
2.行为操作:
- 立即执行;
- 无返回值或返回不是rdd类型。
二、RDD的依赖关系:
1.Lineage:血统、遗传
- RDD最重要的特性之一,保存了RDD的依赖关系;
- RDD实现了基于Lineage的容错机制。
2.依赖关系:
- 宽依赖:一个父RDD的分区被子RDD的多个分区使用。
- 窄依赖:一个父RDD的分区被子RDD的一个分区使用。
3.宽依赖和窄依赖对比: - 宽依赖对应shuffle操作,需要在运行时将同一个父RDD的分区传入到不同的子RDD分区中,不同的分区可能位于不同的节点,就可能涉及多个节点间数据传输。
- 当RDD分区丢失时,Spark会对数据进行重新计算,对于窄依赖只需重新计算一次子RDD的父RDD分区。
- 相比于宽依赖,窄依赖对优化更有利。
4.常用宽窄依赖:
i.宽依赖:distinct、reduceByKey、groupByKey、sortByKey、join;
ii.窄依赖:map、flatMap、filter、union。
三、DAG工作原理:
根据RDD之间的依赖关系,形成一个DAG(有向无环图)。
DAGScheduler将DAG划分为多个Stage:
- 划分依据:是否发生宽依赖(Shuffle);
- 划分规则:从后往前,遇到宽依赖切割为新的Stage;
- 每个Stage由一组并行的Task组成。
四、spark的stage:
stage阶段,DAG 根据是否发生shuffle,将任务分成阶段 ,目的为了实现数据本地化和任务并行化,每一个阶段是一个任务集,任务的个数是此阶段内算子 X的 partition个数。
五、spark的shuffle:
分区之间重新分配数据,注意洗牌是数据的移动,要写入磁盘需要使用网络,造成性能下降,还可能发生数据倾斜。
Shuffle过程:
在分区之间重新分配数据:
- 父RDD中同一分区中的数据按照算子要求重新进入子RDD的不同分区中;
- 中间结果写入磁盘;
- 由子RDD拉取数据,而不是由父RDD推送;
- 默认情况下,Shuffle不会改变分区数量。
六、RDD优化:
1.RDD持久化:
- RDD缓存机制:缓存数据至内存/磁盘,可大幅度提升Spark应用性能;
- 缓存策略StorageLevel:MEMORY_ONLY(默认)、MEMORY_AND_DISK、DISK_ONLY
val u1 = sc.textFile("file:///root/data/users.txt").cache
u1.unpersist() // 去持久化
a.缓存应用场景:
- 从文件加载数据之后,因为重新获取文件成本较高;
- 经过较多的算子变换之后,重新计算成本较高;
- 单个非常消耗资源的算子之后。
b.使用注意事项:
- cache()或persist()后不能再有其他算子;
- cache()或persist()遇到Action算子完成后才生效。
c.检查点:类似于快照:
sc.setCheckpointDir("hdfs:/checkpoint0918")
val rdd=sc.parallelize(List(('a',1), ('a',2), ('b',3), ('c',4)))
rdd.checkpoint
rdd.collect //生成快照
rdd.isCheckpointed
rdd.getCheckpointFile
d.检查点与缓存的区别:
- 检查点会删除RDD lineage(血统),而缓存不会;
- SparkContext被销毁后,检查点数据不会被删除。
2.RDD共享变量:
a.广播变量:允许开发者将一个只读变量(Driver端)缓存到每个节点(Executor)上,而不是每个任务传递一个副本;
val broadcastVar=sc.broadcast(Array(1,2,3)) //定义广播变量
broadcastVar.value //访问方式
注意事项:
- Driver端变量在每个Executor每个Task保存一个变量副本;
- Driver端广播变量在每个Executor只保存一个变量副本。
b.累加器:只允许added操作,常用于实现计数。
val accum = sc.accumulator(0,"My Accumulator")
sc.parallelize(Array(1,2,3,4)).foreach(x=>accum+=x)
accum.value
3.RDD分区设计:
a.分区大小限制为2GB;
b.分区太少:
- 不利于并发;
- 更容易受数据倾斜影响;
- groupBy, reduceByKey, sortByKey等内存压力增大。
c.分区过多:
- Shuffle开销越大;
- 创建任务开销越大。
d.通常情况下:
- 每个分区大约128MB;
- 如果分区小于但接近2000,则设置为大于2000。
4.数据倾斜:
a.指分区中的数据分配不均匀,数据集中在少数分区中:
- 严重影响性能;
- 通常发生在groupBy,join等之后。
b.解决方案:使用新的Hash值(如对key加盐)重新分区。