4.1 Shuffle 六步分解


文档摘要

4.1 Shuffle 六步分解 本节摘要:Shuffle 是 Map 与 Reduce 之间的数据迁徙,横跨两岸共六步:Map 端的环形缓冲、溢写排序、分段归并,Reduce 端的分区拷贝、多路归并、按键分组。它是 MapReduce 性能的主战场——磁盘与网络的大头开销都发生在这里。本节逐步拆解并标注参数与调优点。 为什么 Shuffle 值得单独一节 一个典型作业的耗时画像里,纯业务计算(map 与 reduce 函数本身)常常只占两三成,剩下的被 Shuffle 相关环节吃掉:Map 端的溢写磁盘写、Reduce 端的网络拉取、再一轮磁盘落盘与归并读。中间数据从生到死要经历的 IO 次数,粗算是"写盘一次、读盘一次、网络一次、写盘一次、读盘一次"——五次搬运。

4.1 Shuffle 六步分解

本节摘要:Shuffle 是 Map 与 Reduce 之间的数据迁徙,横跨两岸共六步:Map 端的环形缓冲、溢写排序、分段归并,Reduce 端的分区拷贝、多路归并、按键分组。它是 MapReduce 性能的主战场——磁盘与网络的大头开销都发生在这里。本节逐步拆解并标注参数与调优点。

为什么 Shuffle 值得单独一节

一个典型作业的耗时画像里,纯业务计算(map 与 reduce 函数本身)常常只占两三成,剩下的被 Shuffle 相关环节吃掉:Map 端的溢写磁盘写、Reduce 端的网络拉取、再一轮磁盘落盘与归并读。中间数据从生到死要经历的 IO 次数,粗算是"写盘一次、读盘一次、网络一次、写盘一次、读盘一次"——五次搬运。这是设计使然:MapReduce 用磁盘换可靠性(任何一环挂了都能重放),代价就是 IO 密集。理解六步,就是理解这份代价逐段花在哪、哪段能省。

图 4.1-1 Shuffle 六步分解透视

图 4.1-1 Shuffle 六步分解透视

逐步细看

第一步,环形缓冲。 第 3 章已详述:map 输出与元信息双端生长,写满八成触发溢写。这里补一个视角:缓冲区是 Map 端唯一的内存缓冲,它的大小直接决定溢写次数,溢写次数决定分段数,分段数决定第三步归并的开销——一条参数引发的连锁,源头就在这。

第二步,溢写排序。 锁定区内排序的键是"分区号优先、键次之"。这个复合排序保证了产出文件天然按分区聚集、分区内按键有序——后面 Reduce 端的归并能高效,全靠这一步把排序前置。Combiner 与中间压缩(第 2.3、3.3 节)也在此环节生效。一次溢写一个文件,任务生命周期可能产出几十上百个。

第三步,分段归并。 任务收尾时把所有分段归并成一个文件。归并是流式的:每段各拿一个读指针,反复取当前最小键写出。sort.factor 控制一次归并的路数,段数多于路数就得多轮归并(先归并出中间段再归并中间段)。产出文件按分区排列,每区内有序——至此 Map 端谢幕。

第四步,分区拷贝。 Reduce 任务启动后,向每一个已完成的 Map 任务发起 HTTP 请求,只拉取属于自己的那个分区。默认有多个并行拷贝线程(参数可调,常见 5 路)。拷贝来的数据先进内存缓冲,超过阈值溢写到 Reduce 任务本地磁盘形成小文件。注意这步的两个性能特征:其一,Reduce 必须等足够多的 Map 完成(默认一个比例阈值)才开始拷贝,太早启动会空耗资源,太晚启动会拖长尾——这是 mapreduce.job.reduce.slowstart.completedmaps 参数的领域;其二,拷贝阶段是 Shuffle 网络开销的实体,中间压缩直接按比例缩减这一段的流量。

第五步,多路归并。 所有分区数据到齐后,Reduce 把磁盘上的小文件与内存剩余做多路归并。若数据量大于归并缓冲,依然边归并边溢写、多轮完成。最终得到一个"按键有序"的数据流,供下一步消费。这一步的内存水线由 reduce 任务堆中 shuffle 缓冲占比参数控制,配太小会多轮落盘,配太大挤压 reduce 函数自身的堆空间。

第六步,按键分组。 归并出的有序流上,框架用比较器判断"新键是否等于上一个键":相等则并入当前组继续喂给同一次 reduce 调用,不等则结束当前组、开启下一次调用。分组比较器默认与排序比较器一致,但可以专门设置得更宽松(比如复合键"用户与时间"排序,但只按用户分组),这正是二次排序的实现机关,4.4 节展开。

把六步串成时间线

注意六步不是严格串行的:Map 端三步随各任务滚动发生;Reduce 端第四步与部分 Map 任务仍在运行的时间窗重叠;第五、六步纯在 Reduce 端内。所以真实作业里,"Shuffle 阶段"的边界是模糊的,监控工具里看到的往往是 Map 进度条与 Reduce 进度条的交错——Reduce 进度条前三分之一的"忙"其实是在拷贝,不是在归约。

一个常见的误读由此澄清:ReduceTask 依赖所有 MapTask 输出并不等于"所有 Map 结束 Reduce 才启动",而是"归约开始前数据必须到齐";拷贝可以提前进场,重叠执行正是框架压总时长的手段之一。

六步上的调优点位

步骤 参数方向 直接收益
加大缓冲区 溢写次数减少
Combiner 与中间压缩 段更小更少
提高归并路数 收尾归并轮数减少
拷贝线程与慢启动比例 网络利用与资源占用平衡
reduce shuffle 内存占比 减少落盘轮次
分组比较器 支撑二次排序等模式

不是每个作业都要调——默认值对典型负载已是良好折中。先看作业计数器(溢写次数、混洗字节数、归并轮数),有异常再对症下药,比无差别抄参数表有效得多。第 6.4 节会给完整的排查清单。

本节要点回顾

  • 六步归属:缓冲、溢写排序、分段归并在 Map 端;分区拷贝、多路归并、按键分组在 Reduce 端。
  • 五次搬运:写盘、读盘、网络、写盘、读盘,可靠性设计以 IO 密集为代价。
  • 排序前置:溢写时的"分区加键"复合排序,让 Reduce 端归并只需流式取最小,免全量重排。
  • 重叠执行:拷贝可先于全部 Map 完成进场,慢启动比例控制这个时机。
  • 分组机关:有序流上用比较器切组边界,宽松分组比较器即二次排序的地基。
  • 调优次序:先读计数器定位异常步骤,再动参数,不抄表。

数据归拢成组之前,先回答"谁去哪个 Reduce"——下一节看分区。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U