Spark有三种shuffle,分别是hash shuffle、sort shuffle、Tungsten Shuffle。
1、HashShuffle
适合小数据的场景,对小规模数据的处理效率比排序的shuffle高。
1) 每一个Mapper创建出和Reducer数目相同的bucket,bucket实际上是一个buffer,其大小为spark.shuffle.file.buffer.kb(默认32KB)。
2) Mapper产生的结果会根据设置的partition算法填充到每个bucket中去,然后再写入到磁盘文件。
3) Reducer从远端或是本地的block manager中找到相应的文件读取数据。
针对上述Shuffle过程产生的文件过多问题,Spark有另外一种改进的Shuffle过程:consolidation Shuffle,以期显著减少Shuffle文件的数量。在consolidation Shuffle中每个bucket并非对应一个文件,而是对应文件中的一个segment部分。Job的map在某个节点上第一次执行,为每个reduce创建bucket对应的输出文件,把这些文件组织成ShuffleFileGroup,当这次map执行完之后,这个ShuffleFileGroup可以释放为下次循环利用;当又有map在这个节点上执行时,不需要创建新的bucket文件,而是在上次的ShuffleFileGroup中取得已经创建的文件继续追加写一个segment;当前次map还没执行完,ShuffleFileGroup还没有释放,这时如果有新的map在这个节点上执行,无法循环利用这个ShuffleFileGroup,而是只能创建新的bucket文件组成新的ShuffleFileGroup来写输出。
3) 不需要额外IO---数据写入磁盘只需一次,读取也只需一次
1) 当partitions大时,输出大量的文件(cores * R),性能开始降低 //cores是CPU个数
2) 大量的文件写入,使文件系统开始变为随机写,性能比顺序写要降低100倍
Reduce去拖Map的输出数据,Spark提供了两套不同的拉取数据框架:通过socket连接去取数据;使用netty框架去取数据。
Reduce拖过来数据之后以什么方式存储呢?Reduce拖过来的数据会放在一个HashMap中,HashMap中存储的也是<key, value>对,key是Map输出的key,Map输出对应这个key的所有value组成HashMap的value。Spark将Shuffle取过来的每一个<key, value>对插入或者更新到HashMap中,来一个处理一个。HashMap全部放在内存中。
Shuffle取过来的数据全部存放在内存中,对于数据量比较小或者已经在Map端做过合并处理的Shuffle数据,占用内存空间不会太大,但是对于比如group by key这样的操作,Reduce需要得到key对应的所有value,并将这些value组成一个数组放在内存中,这样当数据量较大时,就需要较多内存。