suffle | 青训营笔记

146 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

shuffle

什么是shuffle : 数据交换过程

shuffle的基本过程是什么 : 经典——Mapreduce

  • map阶段

    1. kv数据再进入缓冲区前要分区,通过Partitioner.getPatition()获取区号

    2. 进入唤醒缓冲区(减少磁盘IO)

    3. (溢写前)当唤醒缓冲区达到80%启动另外线程spill,期间map可以继续写往缓冲区写数据,在锁定的80%数据,对各分区内的数据对 key 进行字典排序(数据在内存中,使用的是快排)

    4. (实行溢写)溢写到磁盘生成临时文件spill文件,已经进行过排序了,溢出的文件是分区且有序的(意思就是分区内有序的)(期间产生很多spill文件)

    5. 可选操作(合并Combiner),如果指定了Combiner,可能会在两个地方被调用:

      • 缓存溢出线程会将缓存存放到磁盘时,会调用Combiner
      • 缓存溢出的数量超过mapreduce.map.combine.minspills(默认为3)时,在缓存溢出文件合并的时候回调用
    6. 对多个spill文件进行合并merge,对分区号排序(对局部有序的文件进行排序,采用归并排序),形成一个按照分区且分区内已经排好序的文件

    7. (溢写结束)删除所有的临时溢出写文件,并告知NodeManger任务已经完成,只要其中一个MapTask完成,ReduceTask就开始复制它的输出(Copy阶段分区输出文件通过http方式提供给[reduce]

    8. 可选操作(压缩)写磁盘时压缩map端的输出,因为这样会让写磁盘的速度更快,节约磁盘空间,并减少传给reduce的数量。默认情况下,输出是不压缩的(将mapreduce.map.ouoput.compress设置为true即可启动)

    • shuffle过程中,map端可自定义的操作

      • 自定义分区,继承 Partitioner
      • 实现Combiner合并(具体步骤与reduce一致)
      • 自定义比较器(自定义排序)(将数据封装到key实现WritableComparable并重写方法)
  • shuffle阶段

  • reduce阶段

    1. (复制)(Reduce如何知道自己应该处理哪些数据?Reduce如何知道数据要从哪台机器上取的map输出?),reduce中的一个线程定期询问master以便获取map输出主机的位置。在一个map任务完成时,知道获得所有输出后reduce任务就开始复制其输出,通过http方式去复制所处理的一个或者多个分区的数据
    2. (归并)复制的数据先进入缓冲区基于JVM的heap size设置,当缓冲超过JVM默认70%则触发写入磁盘(内存到磁盘,如果所有的map输出都没有超过则数据只存在内存),注意的是一边拷贝一边针对键进行归并排序,一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。最终Reduce shuffle过程会输出一个整体有序的数据块
    3. (reduce阶段,分组)完成复制和排序后,默认会根据排好序的key构造对应的value迭代器,也就是分组
    4. (进入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排序的设计,同时仅在指定分区内部排序的情况下才会进行全局排序。