深度理解篇之SparkShuffle

SparkShuffle

对于shuffle的话,对于1.6以前的版本,有一个shufflemanager,shuffle管理器。

Spark在传统的模式中分为hashManager和常用的sortshuffleManager。

简单来说hashShuffle是什么原理呢?

他是一种简单的粗暴的方式,也就是map的一个任务,读到一条数据,该存的时候,首先要算出hash,算出去哪个分区,这时候就会涉及到分区器,算出它的分区号,那么有多少个分区就会有多少个小文件,这条记录是属于哪个分区文件的,那么就去哪个文件,写完之后他是没做合并的,没有合并会带来什么后果呢?随机读写会造成速度低下,所以这是一个很大的问题,所以后续是把这种模式淘汰了。

而在SortShuffleManager当中,它的shuffle写分为三种。

第一个就是bypass模式,就是对曾经shuffle写的方式,也就是HashManager的一种升级,这个过程也是准备不同分区数的小文件,也是要计算分区号,只不过在这个环节上面多加了一步,最后将这些小文件线性拼接成一个文件,这个文件的特征就是分区有序,所以把这种最终造成分区有序文件的这种模式叫为sortShuffleManager,并不是代表排序的意思,而是最终将一写shuffle后的小文件构成一种分区有序,以便于下游寻址读取这个文件,sortshuffleManager这点最重要的语义就是最终是一个文件。这个文件的特征是分区有序。合并成一个文件的这个成本不高,这个过程只是在传统的hashshuffle的过程多了一个合并的过程,极大地提高了后续获取数据的过程,这种模式什么时候触发呢?

就是在reduce端不需要聚合的时候,才会触发,像reduceBykey,不会进行聚合的,reducebykey如果相同的数据有10条,最后会相同的key进行汇聚成一条,value会形成一条,向groupBykey,repartition,sortbykey,会触发这种bypass模式。如果groupBykey会有10条,那么value会进行行列转换,把很多行变为若干列,体积不会发生太大的变化,如果groupBykey不使用bypass的话,就是一种浪费。combiner的目的就是让前面数据变小为了后续shuffle中数据的拉取,groupbukey就没有使用这个优势,value没有变小,groupbykey默认就会走bypass,不会用它的sort的那种内存缓存,repartition更是,只是重新分区,这时候也不需要在内存开辟内存空间,不会将相同的key合到一起,所以会使用bypass这种机制,所以说bypass这种机制是非常快的一种方式,不需要内存的损耗,开辟内存空间,那么sortbykey也是一样的,只要是不需要combiner了,都是触发bypass。还有另外一种特点,就是分区数小于200,也就文件描述符的问题了,可能分区数有八千九千个,文件描述符开启不了那么多,那么这时候就可以不适用bypass,groupBykey这些算子直接使用内存缓冲的,这样会消耗一点内存和cpu的分配,但会减少文件描述符的开辟,也就是拿一个换一个,这是shuffle写的第一个模式Bypass。

还有一种就是堆外内存,这时候写的时候,就像我们之前说的,bypass这种形式的,不需要加压缓存计算的这种东西的,分区数大于200的话, 也可以走onseef,这种模式也就是直接将数据直接写到堆外内存,堆外内存的好处是什么好处呢?堆外内存就是被cpu直接寻址到堆外内存空间了,也就是一对字节数组,也就是将数据序列化扔到堆外内存,最后溢写形成一个文件,如果这个时候物理服务器有100多个G,内存调的很完美,executor只消耗4G内存,那么还有100G还可以使用,那么jvm还可以使用100多个G作为堆外内存来使用,如果这个时候 将数据存到堆外内存中,直接形成一个文件,如果加上零拷贝,这就非常快就了,然后直接怼到网卡发出去。

所以其实堆外内存是现代企业中jvm使用中最为迫切的一种方式。比如非常流行的netty框架,既使用的NIO又使用了堆外内存的零拷贝,一切的都是为了保证这个速度。因为jvm是虚拟机,给他4个G,但是物理层是很大的。我们都知道的jdk1.8的更新也是在堆外内存下了很大的功夫,以上叙述都是bypass和onseef不需要map端聚合的,但是如果需要聚合,需要combiner,那么就会触发第三种模式。SortBase,

假设在内存当中,来一堆数据,我开启一个hashmap,hashmap具备了key唯一的特征,在这时候有一个算子就是combinerBykey是非常重要的,其实groupBykey,reduceBykey底层都是调的combinerByKey,combinerBykey有三个匿名函数需要传递,这三个匿名函数分别是什么呢?

如果在一个任务中,比如说只有<beijing,1>,<beijing,1><beijing,1>这样的数据,你像做的是reduceBykey统计这个数量,那么在这一个任务里面,加了combiner,结果会变成<beijing,3>,如果这时候有一堆数据有50个<beijing,1>,里面都是这样的键值对,给你个hashmap,最后输出<beijing,50>,该怎么做呢?先拿出第一条<beijing,1>,查看hashmap中是否存在beijing的key,如果存在则取出key是biejing的value,给他加1,然后存到hashmap中。比如数偶在分布式的情况下,由于数据比较多,只能存储一定量的数据,那么hashmap会溢写几次,在多个节点上,最后还需要将不同节点的数据聚合一次,比如<beijing,10>在一个节点上,<beijing,80>在一个节点上,做聚合就会变成<beijing,90>。

知道了这种场景,那么我们combinerBykey到底传递那几个函数呢?

第一个匿名函数就是:

数据集的第一条数据,也就是数据进入hashmap的那个过程。

第二个匿名函数就是如果这条记录已经存过了,那么下一条数据怎么计算呢?就是比如_+_,也就是相同的key的值和新值求和的匿名函数。

第三个如果reduce有多次溢写,或者有好多个<beijing,1>,那么就是将它合并<beijing,1>也就是和第二个一样。

这就是combinerBykey的三大匿名函数,这也说明了,sotbass的shuffle写的过程中开辟一个类似与hashmap这样的键值对,因为hashmap它的key是唯一的,所以只动用第一条直接放,第二条进行计算,以及多次溢写时相同的key是如何合并的,这个内存模型和Mapreduce一样吗?

不一样的。在Spark当中bypass,onself,sort没有一个和mapreduce是一样的,他是很丰富的,但是hashmap里面除了做combiner之外,还有一个就是sortbase里面的写,还有一个是buffle,这个才和mapreduce一模一样。等于最后得出一个结论,Spark shuffle现在的新版本已经集成了mapreduce的buffer机制,还是自己的hashmap机制,有自己曾在bypass直接写文件,直接合并的机制,还有堆外内存的方式,若干种shuffle写,所以说它的shuffle系统是一个非常复杂的机制,它可以满足你不同的算子的不同的计算需求,算子是否要map端聚合,下游分区的的数量.

哪种场景会触发spark和mapreduce一样的shuffle呢?

sortbase的hashmap和基于buffer的场景,什么时候会触发呢?如果没有map端聚合,分区数大于200,肯定不走bypass,第二,序列化器又不支持堆外内存,那么onself也不能走了,那么这样的数据应该使用哪中shuffle写呢?那么在sortbase里面又有另外一种机制buffer机制,这个buffer和我们的mapreduce机制是一样的。你来一条数据直接放到bufflle内存,等到内存满了的时候,就会溢写排序,这时候要做的是分区有序,分区内的key有序,这个时候shuffle可以保证你的所有环节。那么区别不同的shuffle写的呢?是根据不同的速度和性能来写的,在分区中需要hashmap吗,是不需要的,用它还有风险,像<beijing,a>,<beijing,b>,<beijing,c>,只需要求分区没必要求hashmap,value是没发求和的,所以这个时候要么就是Bypass,要嘛就是buffle机制,或者onseef,这三个里面挑一个最快的。但是这几种shuffle写都有最终的一种特征,都会生成一个独立的文件,这个文件有一个特点就是分区有序,不像mapreduce一样,分区有序切分区内key有序,因为在mapreduce当中要将相同的key为一组进行后续的处理,但是在前面的bypass也好hashmap也好尤其在hashmap中,已经将相同的key的数据合成了一条了,相同的key是没有若干条的,所以只需要满足分区有序就可以了这是他的shuffle写,这是他的shuffle写。

Sortbykey在map端是没有排序的。

概要:

  1. SparkSumit,之后首先叙述client模式和standalone,
  2. 自己的类是在哪执行的?
  3. New SparkContext,做了那些事情,SparkContext分为资源调度和任务调度,

资源调度就是:申请executor,executor向Master反向注册.

  1. 算子的转换,当遇到action算子的时候, 触发执行,
  2. 然后会将最后一个RDD放在递归模型里,进行计算
  3. DAGSchulder按照shuffle依赖和窄依赖进行切割,划分stage,stage里面就是最后的RDD.
  4. 递归处理了之后,回归的时候stage就会线性执行,那么stage里面最后那个RDD个数就是任务的并行度。并且将这些task传递给tasKScheduler.
  5. TaskScheduler会将这个task进行分发出去到executor。
  6. Task是向taskSchdulder进行移动,
  7. 移动之后进行pipeline迭代嵌套计算模式

那么到任务就会有shuffle写,和suffer读。Shuffle写又bypass,有onseek,有sourtbase里面的hashmap和buffer。不同的算子会触发不同的shuffle写形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值