初始RDD分区个数由Split个数决定(老师说若读取HDFS初始也参考spark.default.parallelism参数指定分区数,如果使用SparkSQL读取Hive或者MySQL数据,初始按照split个数,不参考该参数),假定为N。执行过程中假如没有执行重分区则分区个数还是N,如果执行到Shuffle,Shuffle分为Map端和Reduce端,Map端的任务个数还是N,Reduce端(下个Stage)默认取spark.default.parallelism的值作为分区数,如果该值未配置,则和上一个Shuffle最末端(Map端)的任务个数一致。Shuffle的Map端结束会将数据溢写到磁盘(猜测非HDFS,先内存)并把该位置告知Driver(MapOutputTrackerWorker对象将mapStatus发给Driver的MapOutputTrackerMaster对象),下个Stage阶段Reduce端Task的MapOutputTrackerWorker向Driver的MapoutputTrackerMaster获取数据位置,然后有BlockTransferService去指定的Executor拉取数据放入20%的Executor内存中,默认启动5个子线程,每次拉取不超过48MB。任务在执行时从指定位置拉取数据(例如知道HDFS具体的Block了吧?)。
Shuffle的Map端写入:假设reduce分区个数为M,将写入M个文件,未经优化的HashShuffleManager每个Map任务都写入M个文件,将产生大量的小文件,优化后的每个CPU核心写入M个文件,下次再有Task写入直接复用原来的文件。现在优化和未优化的HashShuffleManager已经淘汰了。现在使用SortShuffleManager,包括两种运行机制,第一是普通运行机制,内存中数据达到阈值后,取10000条进行排序然后通过Java的BufferedOutputStream(自动内存缓存,优化IO性能)写入到单独的文件,写入完成之后所有的文件合并成一个大文件和一个索引文件(对应Partition的索引?),这里不针对Reduce端任务数分文件了。在Map端还会针对reduceByKey、join算子进行Map端的处理;第二种是Bypass方式,当Map端的任务数量小于spark.shuffle.sort.bypassMergeThreshold参数值时启用,写入时也会根据Hash产生M个文件,写入方式先写入缓存,然后溢写到磁盘文件,这个过程和未经优化的HashShuffleManager基本一模一样,不同之处是最后会合并成一个文件和索引文件。