公司真题库

【字节跳动】MapReduce Shuffle 与 Spark Shuffle 原理对比

91学AI·2026/7/20·6 阅读

考察点

这道题出自字节跳动数据仓库工程师一面,是大数据组件原理题里的常青树。面试官想确认你对"shuffle"这个词不是只会背一句"洗牌分发",而是能说清两个框架各自在 map 端写什么文件、reduce 端怎么拉、排序发生在哪。追问常往这几个方向走:Spark 为什么要取消强制排序、sort-based shuffle 相比 hash shuffle 解决了什么问题、bypass 和 Tungsten sort 各自的触发条件。

参考答案

相同点:骨架是一样的

不管 MR 还是 Spark,shuffle 的本质都是把 map 端的输出按 key 重新分发到 reduce 端,骨架完全一致:map task 把输出按分区规则(默认 hash 取模)切分,先写本地磁盘;reduce task 启动后通过 HTTP(或 Netty)从各个 map 节点拉取属于自己的那份数据,归并后交给 reduce 逻辑。两边都要解决同样的问题:磁盘 IO、网络传输、内存缓冲、小文件数量。说清这个共同骨架,再谈差异,会显得你理解的是本质而不是背了两段孤立的描述。

MapReduce 的 shuffle:强排序

MR 的 map 端,每条记录先进入内存环形缓冲区(io.sort.mb 控制大小,默认 100MB),缓冲区写到 80%(io.sort.spill.percent)就触发溢写。溢写前在内存里做两件事:按 partition 分区、partition 内按 key 排序,然后用 combiner(如果配了)做本地聚合,最后落成一个排好序的小文件。一个 map task 可能溢出多个小文件,最后 merge 成一个大文件,带索引标明每个 partition 的偏移。

reduce 端通过 HTTP 从所有 map 节点拷贝对应 partition,拷贝来的小文件先放内存,放不下了 spill 到磁盘,全部拉完后做多路归并排序——归并时天然利用了 map 端已经排好序的有序性。所以 MR 的 shuffle 是"强制排序":不管你 reduce 逻辑需不需要有序,排序的成本都已经付出了。这个设计来自 MR 的哲学——reduce 收到的本来就该是按 key 分组有序的数据流。

Spark shuffle 的三代演进

Spark 第一代的 hash shuffle 最接近"裸奔"版:每个 map task 给每个 reducer 写一个文件,M 个 map、R 个 reducer 就产生 M×R 个文件。它不做排序,省去了排序成本,但文件数爆炸——一万个 map 乘一千个 reducer 就是一千万个小文件,文件句柄和磁盘随机 IO 都扛不住。后来的 consolidated hash shuffle 做了优化,同一个 executor 上的 task 复用一组文件,文件数降到 cores×R,缓解了但没根治。

Spark 1.2 起 sort-based shuffle 成为默认:map 端把记录写进内存数据结构,按 partition id(可选加 key)排序后溢写,最后合并成一个数据文件加一个索引文件——每个 map task 只产出两个文件,文件数从 M×R 降到 M。reduce 端拉取后按需归并。注意这里的排序主要是为了让一个文件内的 partition 连续、好切分,如果算子本身不要求排序(比如 reduceByKey 不需要全局有序),可以省掉 key 排序。

两个特例值得一提。bypass merge sort:当 reducer 数小于阈值(spark.sql.shuffle.partitions 相关,默认 200)且没有 map 端聚合时,退化为按 partition 分别写临时文件再合并,跳过排序,因为小文件量可控时排序是纯浪费。Tungsten sort shuffle(unsafe row):数据以序列化二进制形式存在堆外内存,直接在二进制上操作,减少对象开销和 GC 压力,这是 Spark 内存管理演进的方向。

核心差异收个尾

面试收尾可以这样归纳:MR shuffle 排序是强制的,因为 reduce 语义建立在有序分组上;Spark 把排序变成了可选项,算子需要才排(sortByKey 要,reduceByKey 不要),代价是 hash shuffle 的文件爆炸问题,最后靠 sort-based + 索引文件收敛。一句话——MR 是"先排好再给你",Spark 是"你要我才排"。

维度MapReduceSpark(sort-based)
map 端文件数每 task 1 数据+1 索引(merge 后)每 task 1 数据+1 索引
排序强制按 key 排按 partition 排,key 排序可选
reduce 端归并多路归并(有序)按需归并
内存结构环形缓冲区 100MB内存 map/unsafe row

可能的追问

  • Spark 为什么能不做排序?因为 RDD 算子语义不依赖输入有序,聚合靠哈希表而不是排序分组,排序只在需要有序的算子(sortByKey、repartitionAndSortWithinPartitions)时才发生。
  • sort-based shuffle 为什么解决了小文件问题?一个 map task 的所有 partition 合并进一个数据文件,靠索引文件记录每个 partition 的起止偏移,文件数与 map 数线性相关而不是乘积。
  • shuffle 数据落盘了还算内存计算吗?算。Spark 的"内存计算"指的是中间结果可以 cache 在内存、DAG 内不必像 MR 那样每步写 HDFS;shuffle 本身为了容错和跨节点传输,该落盘还是落盘。

评论 (0)

暂无评论,快来抢沙发吧!

91学AI

© 2026 91学AI · 按岗位学 AI 与大数据. All rights reserved.