好的,我们来详细讲解一下 Hive 中 Shuffle 的基本流程。
首先,要明确一个核心概念:Hive 本身不处理数据计算,它只是将 SQL 查询转换成一个或多个 MapReduce、Tez 或 Spark 作业。因此,Hive 中的 Shuffle 过程本质上就是其底层计算引擎的 Shuffle 过程。
由于 MapReduce 是最经典、最能清晰解释 Shuffle 概念的模型,我们以 Hive on MapReduce 为例来详细说明。其基本流程如下图所示,整个过程发生在 Map 阶段之后、Reduce 阶段之前:
详细步骤分解
整个 Shuffle 过程可以分为 Map 端 和 Reduce 端 两大部分。
一、Map 端处理
Map 任务的主要职责是处理输入数据,并为 Reduce 任务准备好数据。
-
读取输入数据 (Read):
- 每个 Map Task 读取 HDFS 上的一个数据切片(Split)。
- 使用
InputFormat(如TextInputFormat)解析数据,生成键值对<k1, v1>。
-
Map 函数处理 (Map):
- Hive 根据 SQL 语句生成 Map 函数(例如,在
SELECT ... WHERE语句中,Map 阶段会进行过滤和投影)。 - Map 函数处理
<k1, v1>,输出新的中间键值对<k2, v2>。Shuffle 的核心就是处理这些<k2, v2>。
- Hive 根据 SQL 语句生成 Map 函数(例如,在
-
分区 (Partitioning):
- 目的:决定每个
<k2, v2>应该被发送到哪个 Reduce 任务进行处理。 - 方法:使用
Partitioner接口(默认是HashPartitioner)。计算key2的哈希值,并对 Reduce 任务数取模:hash(k2) mod R(其中 R 是 Reduce 任务的数量)。 - 结果:数据被分成 R 个分区(Partition),每个分区对应一个 Reduce 任务。
- 目的:决定每个
-
溢写 (Spill to Disk):
- 循环缓冲区 (Circular Buffer):Map 任务输出不会直接写入磁盘,而是先写入一个内存中的环形缓冲区(默认大小 100MB)。
- 阈值溢写 (Threshold Spill):当缓冲区内容达到一定阈值(默认 80%)时,一个后台线程会开始将这部分数据溢写 (Spill) 到本地磁盘。
- 排序 (Sort):在溢写之前,会对这个分区内的数据按照 key2 进行排序(如果指定了
Combiner,也会在这个阶段执行)。这样,每个溢写文件都是分区内有序的。
-
合并 (Merge):
- Map 任务可能会生成多个溢写文件(如果 Map 输出很大,会触发多次溢写)。
- 在 Map 任务结束之前,所有这些溢写文件会被合并成一个大的、已排序的输出文件。
- 注意:这个合并后的文件仍然是按分区排序的,并且每个分区内部是按 key 排序的。
至此,Map 端的工作全部完成。最终,每个 Map 任务会生成一个数据文件,这个文件中包含了所有分区且分区内有序的数据。这个文件存储在 Map 任务所在节点的本地磁盘上。
二、Reduce 端处理 (Shuffle & Sort)
Reduce 任务的主要职责是拉取属于自己的数据,并进行处理。
-
拉取数据 (Fetch/Copy):
- Reduce 任务启动后,会向 Application Master 询问哪些 Map 任务已经完成。
- 然后,Reduce 任务通过 HTTP 请求连接到各个已完成 Map 任务所在的节点,将属于自己的分区数据拉取(Copy)到本地。
- 这个过程是 “拉取 (Pull)” 而不是“推送 (Push)”。
-
合并阶段 (Merge Phase):
- 由于数据来自多个 Map 任务,Reduce 端会同时拉取多个数据流。
- 为了减少磁盘 I/O,拉取的数据会先写入内存缓冲区。如果内存不足,也会像 Map 端一样溢写到磁盘。
- 在数据不断拉取和溢写的过程中,会有一个后台线程将多个小的磁盘文件合并 (Merge) 成更大的、有序的文件。这个过程可能会进行多轮(多个阶段)。
-
排序 (Sort):
- 在最后的合并阶段(通常是将磁盘文件和内存中最后一批数据合并),所有数据会被统一进行一次归并排序 (Merge Sort)。
- 最终,Reduce 任务的输入是一个整体有序的大文件。也就是说,传入 Reduce 函数的数据是按 key 分组且排序好的(例如,所有相同的
keyA都相邻排列)。
-
Reduce 函数处理 (Reduce):
- 这个有序的数据流被传递给 Reduce 函数。
- 因为数据已经按 key 分组,所以 Reduce 函数可以很容易地迭代处理每个 key 对应的所有 values(例如,计算 SUM、COUNT 等)。
- Reduce 函数处理完成后,将最终结果
<k3, v3>写入 HDFS。
总结与核心要点
| 阶段 | 核心操作 | 目的 |
|---|---|---|
| Map 端 | 分区 (Partitioning) | 确定数据去往哪个 Reduce |
| 排序 (Sort) | 使每个分区内数据有序,为 Reduce 端归并排序做准备 | |
| 可选合并 (Combiner) | Map 端本地聚合,减少网络传输量 | |
| Reduce 端 | 拉取 (Fetch/Copy) | 将属于自己分区的数据从所有 Map 端拉取过来 |
| 归并排序 (Merge Sort) | 将所有 Map 端来的数据整合成一个有序的整体 | |
| 分组 (Grouping) | 将相同 key 的 value 形成迭代器,传递给 Reduce 函数 |
- Shuffle 的代价:Shuffle 涉及大量的磁盘 I/O(溢写、合并)、网络传输(跨节点拉取数据)和CPU 开销(排序、序列化/反序列化)。它是 MapReduce 作业中最昂贵、最慢的一个阶段,是优化的重点。
- Hive on Tez/Spark:在现代的 Hive 执行引擎(如 Tez 或 Spark)中,Shuffle 的基本思想(洗牌、分区、排序)是类似的,但实现机制有所不同。它们会进行大量优化,例如:
- Tez:可能会避免将中间数据写入磁盘,而更多地在内存中处理,并使用更高效的网络传输协议。
- Spark:提供了更灵活的内存管理模型(内存、磁盘、堆外内存),并且其 Shuffle 实现(如 Sort Shuffle 或 Tungsten Sort)也经历了多次优化以提升性能。
希望这个详细的解释能帮助你彻底理解 Hive 中的 Shuffle 流程!
Hive Shuffle流程详解

1494

被折叠的 条评论
为什么被折叠?



