MapReduce Join 操作:大表关联小表用 Map 端,大表关联大表用 Reduce 端
数据处理中,多表关联是家常便饭——订单关联用户、日志关联商品、销售关联库存。在 SQL 里一个 JOIN 就搞定,在 MapReduce 里得自己实现。
MapReduce 提供了两种 Join 方案:
- Reduce 端 Join:通用,任何规模都能用,但要走 Shuffle,性能一般
- Map 端 Join:快,但要求一张表足够小,能塞进内存
怎么选?就一句话:小表能装进内存就用 Map 端 Join,装不下就用 Reduce 端 Join。
Join 的核心难点:怎么让相同 Key 的数据到同一个地方
两个表关联,关键是把相同关联键的数据凑到一起。
- 订单表有商品 ID → 需要找到商品表里相同商品 ID 的信息
- 问题是:订单数据分散在不同节点,商品数据也分散在不同节点
- 怎么让同一个商品 ID 的订单数据和商品数据落到同一个节点?
这就是 Join 的核心挑战。MapReduce 用两种方式解决:
| 方案 | 怎么凑到一起 | 前提 |
|---|---|---|
| Reduce 端 Join | 通过 Shuffle,相同 Key 自动分到同一个 Reduce | 无限制 |
| Map 端 Join | 小表提前加载到每个 Map 任务的内存里 | 小表能装进内存 |
Reduce 端 Join:通用方案,任何规模都能用
实现原理
flowchart TD
A[表 A 数据] -->|Map 阶段| B["打标签 "]
C[表 B 数据] -->|Map 阶段| D["打标签 "]
B --> E[Shuffle 按 Key 分组]
D --> E
E --> F["Reduce 阶段合并 "]
F --> G[输出关联结果]
思路:
- Map 阶段:从不同表读数据,统一输出
<关联键, 来源标记 + 数据> - Shuffle 阶段:相同关联键自动分到同一个 Reduce
- Reduce 阶段:把同一 Key 的两张表数据合并
关键点:Map 阶段一定要标记数据来源。 Reduce 阶段收到的是同一个 Key 的 value 列表,不知道哪些来自订单表、哪些来自商品表,所以需要标记(比如用 “order” 和 “product” 前缀)。
代码骨架:
Map 阶段:
1 | public void map(LongWritable key, Text value, Context context) { |
Reduce 阶段:
1 | public void reduce(Text key, Iterable<Text> values, Context context) { |
优缺点:
| 优点 | 缺点 |
|---|---|
| 数据量无限制,什么都能 Join | 所有数据都要走 Shuffle,网络传输大 |
| 支持内连接、左连接、外连接 | Reduce 端需要排序,开销大 |
| 实现直观,好理解 | 大表 Join 大表时,Reduce 是瓶颈 |
Map 端 Join:快,但小表得能装进内存
实现原理
flowchart TD
A[小表数据] -->|Map 初始化| B["加载至内存 Map"]
C[大表数据] -->|Map 阶段| D["读取大表记录,通过内存 Map 关联"]
D --> E[直接输出关联结果]
思路: 把小表加载到每个 Map 任务的内存里,Map 读大表时直接在内存里查,不需要 Shuffle 和 Reduce。
前提条件: 小表必须能装进 MapTask 内存(通常 < 1GB)。如果装不下,Map 端 Join 不适用。
代码骨架:
setup 阶段加载小表:
1 | private HashMap<String, String> productMap = new HashMap<>(); |
Map 阶段查内存:
1 | public void map(LongWritable key, Text value, Context context) { |
Driver 里把小表放进分布式缓存:
1 | job.addCacheFile(new URI("hdfs:///input/product.txt")); |
优缺点:
| 优点 | 缺点 |
|---|---|
| 无 Shuffle,无 Reduce,性能极高 | 小表必须能装进内存,否则 OOM |
| 网络传输少 | 只适合”一大一小”的场景 |
| 代码比 Reduce 端 Join 简单 | 不支持大表 Join 大表 |
实际项目中的经验:
- 小表 < 100MB → 无脑 Map 端 Join
- 小表 100MB - 500MB → Map 端 Join,把 MapTask 内存调到 2GB 以上
- 小表 > 500MB → 先用 Map 端 Join 试,不行换 Reduce 端
- 两张都大 → Reduce 端 Join,同时用 Combiner 和压缩优化