Hadoop 中的 Join
在 Hadoop 生态体系里,对数据集的 Join 操作可能是最重要的操作之一了。因此了解 Hadoop 中的 Join 的实现原理从下面几个方面来考虑是很有必要的:
- 在实际场景中该使用哪种 Join 策略;
- Join 过程中遇到问题该如何 debug;
- 更好地使用 Hive;
- 在需要更细粒度的操作时可以手撸代码。
总的来说,Hadoop 中的 Join 可以分为 2 类:
- Reduce-Side
- Map-Side
Reduce Side Joins
这种模式的 Join 操作在每个 Reducer 中进行而不是在 mapper 中。
在三种 Join 模式中,这是最容易实现的一种,因为 排序和 shuffle 的过程全部由 MapReduce 框架帮助完成了。

基本上,Reduce Side Join 操作的顺序如下所示:
- Mapper 读入数据,以 join column 为 key;
- mapper 给输入的数据打上标签: 这条数据属于哪个数据集或者数据库;
- mapper 输出的中间键值对的键就是 join column;
- MapReduce 框架对中间键值对做排序和 shuffle 处理,输出的 join column 键和对应的值列表会被当做 reducer 的输入;
- reducer 对输入的数据做接下来的聚合处理。
具体的代码参见 这个 Repo。
结论
Reduce Side Join 实现简单,逻辑清晰,而且对 map 的输入数据几乎没有任何限制。
但是,在 reducer 端做 join 有很大的问题,由于完全将 sort 和 shuffle 操作交给 MapReduce 框架实现,因此会占用大量(昂贵的)带宽资源,并且当数据量大的时候会占用很多内存资源,极有可能造成 OutOfMemory Exception。
Map Side Join
假设有两个表,其中一个表的数据量比较小。在 Map Side Join中:
一个本地的 MapReduce 任务会先把小的表的数据文件读到一个内存 Hash 表中,然后会将这个表中的数据序列化到一个Hash 表文件中。
这一步的必要性在于:如果没有 cache, 当有几千个 mapper 同时向 HDFS 读取原始小表数据时,这个过程必将成为瓶颈。原始的 MapReduce 任务将存在 Hash 表文件 中的数据转移到 Hadoop 分布式缓存中,然后将这些文件发送到每个 mapper 的本地磁盘上;
每个 mapper 将存在本地的小表数据序列化到内存中然后做 join 操作。

优缺点
优点
缺点
- 只有当两个表中的一个小到足以塞进 mapper 的 内存中时才有用。