大数据处理中绕不开的是两个甚至多个表的Join操作,由于要进行Shuffle操作,所以比较耗费时间,本文介绍了针对大小表Join的优化方法Map-Join,利用Hadoop提供的DistributedCache来进行缓存小表文件,然后再Mapper中直接进行Join,从而避免了Shuffle操作,加速了Join,具体实现可以看我的github
基础数据
有两张表,分别是用户信息数据以及用户页面的日志数据,建表语句如下所示:
-- 用户行为数据
CREATE TABLE `tmp.log_user_behavior`(
`user_id` string COMMENT '用户id',
`item_id` string COMMENT '商品id',
`category_id` string COMMENT '商品分类id',
`behavior` string COMMENT '行为',
`ts` date COMMENT '行为发生时间')
PARTITIONED BY (
`date` string)
231758,3622677,4758477,pv
92253,642337,4135185,pv
297958,1762578,4801426,pv
786771,1940649,1320293,pv
789048,3144191,2355072,pv
895384,1138468,1602288,pv
578800,1324176,4135836,pv
886777,4606952,996587,pv
-- 用户信息
CREATE TABLE `tmp.base_user_info`(
`user_id` string COMMENT '用户id',
`user_name` string COMMENT '用户名字')
PARTITIONED BY (
`date` string)
66985,name-66985
332113,name-332113
102932,name-102932
874086,name-874086
我们想要获取在用户行为信息上面添加上用户name信息,用sql实现如下:
select a.user_id, a.item_id, a.category_id, a.behavior, a.ts, b.user_name
from tmp.log_user_behavior a
join tmp.base_user_info b
on a.user_id = b.user_id
where a.date='20200831' and b.date='20200831'
本文从MR角度来实现Map端Join编程方法和思路。
Map端join
原理介绍
Map side join是针对以下场景进行的优化:两个待连接表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多份,让每个map task内存中存在一份(比如存放到hash table中),然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否有相同的key的记录,如果有,则连接后输出即可。
为了支持文件的复制,Hadoop提供了一个类DistributedCache,使用该类的方法如下:
- 用户使用静态方法DistributedCache.addCacheFile()指定要复制的文件,它的参数是文件的URI(如果是HDFS上的文件,可以这样:hdfs://namenode:9000/home/XXX/file,其中9000是自己配置的NameNode端口号)。JobTracker在作业启动之前会获取这个URI列表,并将相应的文件拷贝到各个TaskTracker的本地磁盘上。
- 用户使用DistributedCache.getLocalCacheFiles()方法获取文件目录,并使用标准的文件读写API读取相应的文件。
不过DistributedCache.addCacheFile()该方法在hadoop2.0之后的版本中已经废弃了,可以使用job.addCacheFile()方法添加缓存文件。
在hql里面相关配置如下:
set hive.auto.convert.join=true
set hive.mapjoin.smalltable.filesize=25000000
UserInfo实现
对于用户信息序列化处理
package com.hadoop.mapreduce.bean;
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java