spark的sortShuffleManager解析

标题sortShuffleManager

一、注册ShuffleHandle的策略
首先,在shuffle过程中满足以下条件,选择BypassMergeSortShuffleHandle:
1)map端没有聚合操作
2)shuffle read partitions <= spark.shuffle.sort.bypassMergeThreshold(阈值默认为200)
其次,满足以下条件,选择SerializedShuffleHandle:
1)序列化方式支持对象重新定位(意思是可以对已经序列化的对象进行排序,这种排序起到的效果和先对数据排序再序列化一致)
2)map/reduce端没有聚合操作
3)shuffle read partitions < 16777216(2^24:原因ShuffleInMemorySorter的成员变量LongArray数组中每个long类型元素只留24位给partitionId,其余40位留给了每条数据所在的page页编号及位置偏移量)
最后,如果不满足上述任意一个条件,就选择BaseShuffleHandle
二、选择shuffleWriter类型
根据ShuffleHandle类型选择shuffleWriter类型,其对照关系如下:
BypassMergeSortShuffleHandle -> BypassMergeSortShuffleWriter
SerializedShuffleHandle -> UnsafeShuffleWriter
BaseShuffleHandle -> SortShuffleWriter
三、不同类型shuffleWriter的适合场景
1)BypassMergeSortShuffleWriter
shuffleWriter期间,每个map task创建reduce个shuffle文件,根据记录key值对应的分区写到相应的文件;数据写完后,每个map task对应的所有中间文件被合并成一个数据文件和一个索引文件,索引文件中的每个Long类型数据代表前n个分区的累积字节量,比如第二个代表第一个分区的字节量,第三个代表前两个分区的字节量,注意第一个表示占位。
多个文件合并成一个文件的方式:使用FileChannel的transferTo方法执行文件流拷贝(NIO方式),NIO有个2G的限制,为了避免2G以上的文件拷贝的完整性,增加了一个循环判断机制。https://blog.csdn.net/MAX_YanBangbang/article/details/97615364
缺点:对每个map task,BypassMergeSortShuffleWriter会同时打开与reduce分区同等个数的序列化器和文件流DiskBlockObjectWriter,即一次会打开多个文件,需要更多的内存分配给缓冲区
https://blog.csdn.net/qq_26222859/article/details/81451126
https://www.cnblogs.com/johnny666888/p/11291592.html
2)UnsafeShuffleWriter
https://blog.csdn.net/zc19921215/article/details/86500383
shuffleWriter期间,每个map task会因多次溢写产生多个临时shuffle文件,这些临时文件最后会被合并为一个shuffle文件。数据溢写过程和shuffle文件合并过程如下:
溢写过程:
该shuffleWriter首先对记录分区,然后序列化后写入MyByteArrayOutputStream的缓冲区(初始容量1M,自动扩容),最后插入ShuffleExternalSorter。在此过程中,会触发多次溢写。
每当记录数达到某一阈值(spark.shuffle.spill.numElementsForceSpillThreshold默认Integer.MAX_VALUE)时,调用ShuffleExternalSorter溢写数据到磁盘,溢写前会对数据进行排序(默认使用RadixSort排序方式,可以通过参数spark.shuffle.sort.useRadixSort配置选择TimSort排序)。
ShuffleExternalSorter用于管理内存的申请和释放,这里的内存是以内存页Page的形式存在。Page个数上限为213,每个page申请的内存为MAX(pageSize,require)的最大值,其中pageSize值为MIN(227byte=128MB, spark.buffer.pageSize)的最小值。在申请内存期间,会按顺序判断以下条件:条件一,如果page申请内存大于(2^31-1)*8byte~17GB(常量MAXIMUM_PAGE_SIZE_BYTES),就会抛出TooLargePageException异常;条件二,如果未申请到所需大小内存,将触发数据溢写过程,首先从其他MemoryConsumer溢写数据释放内存,释放的内存仍然不够才会选择从当前MemoryConsumer释放内存,这样可以减少溢写次数,避免产生太多的溢写文件。
PS:
(1)spark.buffer.pageSize是spark的配置参数,其默认值是根据资源情况动态计算获得,位于1M~64M之间,也可以配置默认值。
(2)MAXIMUM_PAGE_SIZE_BYTES是TaskMemoryManager类的常量,其值为(2^31-1)*8L,
ShuffleInMemorySorter是专门用来给数据排序的,它内部包含了一个存储数据指针的LongArray数组,存放原信息编码后的数据,原信息为partitionId + pageNum + offsetInPage,它们的长度分别是24,13,27位。LongArray数组初始容量的大小为4096,最大为Integer.MAX_VALUE(由溢写记录上限阈值决定),该数组的可用空间由排序方式决定,默认排序方式RadixSort可用空间占总空间的1/2,TimSort占2/3,当可用空间达到该阈值,就会对数组进行扩容或溢写数据到磁盘。溢写数据前,按partitionId对数据做排序。然后循环遍历排序后的LongArray数组,从编码后的原信息数据中反解出当前记录所在内存Page页编号及Page页中的偏移offset,分批次将记录从内存页中拷贝到字节数组对象中并溢写到临时文件中,每批次拷贝的最大数据量为字节数组的大小,字节数组的大小由参数spark.shuffle.spill.diskWriteBufferSize配置,默认值为1MB,溢写完数据后释放内存页并重置LongArray数组。每次溢写完数据都会生成一个临时文件,该临时文件对应一个SpillInfo对象,里面记录着临时文件所在磁盘位置、所有分区下每个分区的数据量。多次溢写创建的SpillInfo对象会被一一放入LinkedList集合中,据此将所有临时文件合并成一个最终文件。
临时shuffle文件合并过程:
一个map task触发多次溢写产生的多个磁盘临时文件,UnsafeShuffleWriter类会对其进行合并处理。合并之前,会将最后一批还未溢写到磁盘的数据按溢写过程写到临时文件,然后开始临时文件的合并过程,根据临时文件个数和IO压缩编码选择不同的合并策略(现有4种压缩编码lz4、lzf、snappy、zstd,默认值lz4)。如果临时文件只有一个,就通过move方式将临时文件变更为最终文件;如果临时文件超过一个,有快速合并和慢速合并两种方式,

你可能感兴趣的:(spark,big,data)