Content #
每个 Map 任务的计算结果都会写入到本地文件系统,等 Map 任务快要计算完成的时候,MapReduce 计算框架会启动 shuffle 过程,在 Map 任务进程调用一个 Partitioner 接口,对 Map 产生的每个 <Key, Value> 进行 Reduce 分区选择,然后通过 HTTP 通信发送给对应的 Reduce 进程。这样不管 Map 位于哪个服务器节点,相同的 Key 一定会被发送给相同的 Reduce 进程。Reduce 任务进程对收到的 <Key, Value> 进行排序和合并,相同的 Key 放在一起,组成一个 <Key, Value 集合 > 传递给 Reduce 执行。
map 输出的 <Key, Value>shuffle 到哪个 Reduce 进程是这里的关键,它是由 Partitioner 来实现,MapReduce 框架默认的 Partitioner 用 Key 的哈希值对 Reduce 任务数量取模,相同的 Key 一定会落在相同的 Reduce 任务 ID 上。从实现上来看的话,这样的 Partitioner 代码只需要一行。
/** Use {@link Object#hashCode()} to partition. */
public int getPartition(K2 key, V2 value, int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
分布式计算需要将不同服务器上的相关数据合并到一起进行下一步计算,这就是 shuffle。
shuffle 是大数据计算过程中最神奇的地方,不管是 MapReduce 还是 Spark,只要是大数据批处理计算,一定都会有 shuffle 过程,只有让数据关联起来,数据的内在关系和价值才会呈现出来。shuffle 也是整个 MapReduce 过程中最难、最消耗性能的地方,在 MapReduce 早期代码中,一半代码都是关于 shuffle 处理的。