这是我参与「第四届青训营 」笔记创作活动的第2天
shuffle
什么是shuffle : 数据交换过程
shuffle的基本过程是什么 : 经典——Mapreduce
-
map阶段
-
kv数据再进入缓冲区前要分区,通过Partitioner.getPatition()获取区号
-
进入唤醒缓冲区(减少磁盘IO)
-
(溢写前)当唤醒缓冲区达到80%启动另外线程spill,期间map可以继续写往缓冲区写数据,在锁定的80%数据,对各分区内的数据对 key 进行字典排序(数据在内存中,使用的是快排)
-
(实行溢写)溢写到磁盘生成临时文件spill文件,已经进行过排序了,溢出的文件是分区且有序的(意思就是分区内有序的)(期间产生很多spill文件)
-
可选操作(合并Combiner),如果指定了Combiner,可能会在两个地方被调用:
- 缓存溢出线程会将缓存存放到磁盘时,会调用Combiner
- 缓存溢出的数量超过mapreduce.map.combine.minspills(默认为3)时,在缓存溢出文件合并的时候回调用
-
对多个spill文件进行合并merge,对分区号排序(对局部有序的文件进行排序,采用归并排序),形成一个按照分区且分区内已经排好序的文件
-
(溢写结束)删除所有的临时溢出写文件,并告知NodeManger任务已经完成,只要其中一个MapTask完成,ReduceTask就开始复制它的输出(Copy阶段分区输出文件通过http方式提供给[reduce]
-
可选操作(压缩)写磁盘时压缩map端的输出,因为这样会让写磁盘的速度更快,节约磁盘空间,并减少传给reduce的数量。默认情况下,输出是不压缩的(将mapreduce.map.ouoput.compress设置为true即可启动)
-
shuffle过程中,map端可自定义的操作
- 自定义分区,继承 Partitioner
- 实现Combiner合并(具体步骤与reduce一致)
- 自定义比较器(自定义排序)(将数据封装到key实现WritableComparable并重写方法)
-
-
shuffle阶段
-
reduce阶段
- (复制)(Reduce如何知道自己应该处理哪些数据?Reduce如何知道数据要从哪台机器上取的map输出?),reduce中的一个线程定期询问master以便获取map输出主机的位置。在一个map任务完成时,知道获得所有输出后reduce任务就开始复制其输出,通过http方式去复制所处理的一个或者多个分区的数据
- (归并)复制的数据先进入缓冲区基于JVM的heap size设置,当缓冲超过JVM默认70%则触发写入磁盘(内存到磁盘,如果所有的map输出都没有超过则数据只存在内存),注意的是一边拷贝一边针对键进行归并排序,一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。最终Reduce shuffle过程会输出一个整体有序的数据块
- (reduce阶段,分组)完成复制和排序后,默认会根据排好序的key构造对应的value迭代器,也就是分组
- (进入reduce)shuffle结束
-
shuffle过程中,reduce端可自定义的操作
- 自定义分组(继承WriteableComparator)
-
关于合并(combine)和归并(merge)的区别————将一部分reduce端的工作拿到map端做
两个键值对 <a,1>,<a,1> 合并的效果会得到:<a,2> 归并的效果会得到:<a,<1,1>>
shuffle触发流程
Collection Action -> SubmitJob -> GetDependencies -> Register Shuffle
val text = sc.textFile("mytextfile.txt")
val counts = text
.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_+_)
counts.collect
- 在 collect Action 中会调用spark中DAG Scheduler的submit Job,
- DAG Scheduler会分析counts对象的RDD依赖关系,由于有reduceByKey操作,所以会创建`ShuffledRDD`,`ShuffledRDD`
- 在DAG Scheduler调用getdependency的时候,会去创建shuffle dependency,从而向shuffle manager注册自己。
- 后续DAG会生成map和reduce两个stage,这两个stage会分别提交给Executor,计算就真正开始了。
Sort Shuffle
Sorted Based Shuffle, 即基于Sorted的Shuffle实现机制,在该Shuffle过程中,Sorted体现在输出的数据会根据目标的分区Id (即带 Shuffle过程 的目标RDD中各个分区的Id值)进行排序,然后写入 一个单独的Map端输出文件中。
相应的, 各个分区内部的数据并不会再根据Key值进行排序,除非调用带排序目的的方法 ,在方法中指定Key值的Ordering实例,才会在分区内部根据该Ordering实例对数据进行排序。
当Map端的输出数据超过内存容纳大小时,会将各个排序结果Spill到磁盘上, 最后再将这些Spill的文件合并到一个最终的文件中。
在Spark的各种计算 算子中到处都体现了一种惰性的理念,在此也是类似,在提升性能需要时,引入根据分区Id排序的设计,同时仅在指定分区内部排序的情况下才会进行全局排序。