Spark分布式计算原理

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加盐)重新分区。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值