在早期版本的Spark中,shuffle过程没有磁盘读写操作,是纯内存操作,后来发现效率较低,且极易引发OOME,较新版本的Shuffle操作都加入了磁盘读写进行了改进。
1、未经优化的HashShuffleManager:上一个stage中每一个task会对下一个stage的每一个task写一份数据文件,假定上一个stage有N个task,下一个stage有M个task,此时由上到下形成N个1对M的映射关系,总共产生【N M】个文件。这种方式的优点是思路简单,数据文件的逻辑隔离性更强。缺点是在磁盘上产生的文件个数太多,每个文件的读写都需要建立管道等操作,过多的文件势必增加额外的开销,效率较低。【同将多个小文件打包为一个大文件再拷贝,比直接拷贝多个小文件更快,一个道理】
2、优化过的HashShuffleManager:上一个stage中每一个task共同写下一个stage的每一个task独有的数据文件,假定上一个stage有N个task,下一个stage有M个task,此时由上到下形成M个N对1的映射关系,总共产生M个文件(文件数量只取决于下一个stage的task数量)。由于文件数量的减少,性能得到了一定的提升。
**
3、SortShuffleManager:这是当前版本中使用的方式,进一步减少数据文件个数,阶段之间只通过2个文件来传递数据【索引文件、数据文件】。在上一个阶段中,每个task都将数据在内存中进行排序生成文件(如果内存不够用就溢写到磁盘),将多个排序后的文件合并到同一个数据文件中,配合索引文件,下游task就能高效的完成读取操作。
由于排序操作是一个相对低效的操作,所以在小数据量时可以使用Hash算法来达到快速定位的目的。此时就轮到bypass机制,其内容是当shuffle-map-task数量小于bypassMergeThreshold(默认200个)时或者不是聚合类shuffle,就不采用排序而换为Hash操作。