Hive中就是把Map,Reduce的Join拿过来,通过SQL来表示。
参考链接:Common/Shuffle/Reduce Join
Reduce Join在Hive中也叫Common Join或Shuffle Join
如果两边数据量都很大,它会进行把相同key的value合在一起,正好符合我们在sql中的join,然后再去组合,如图所示。Map Join
1) 大小表连接:
如果一张表的数据很大,另外一张表很少(<1000行),那么我们可以将数据量少的那张表放到内存里面,在map端做join。
Hive支持Map Join,用法如下select /*+ MAPJOIN(time_dim) */ count(1) fromstore_sales join time_dim on (ss_sold_time_sk = t_time_sk)
2) 需要做不等值join操作(a.x < b.y 或者 a.x like b.y等)
这种操作如果直接使用join的话语法不支持不等于操作,hive语法解析会直接抛出错误
如果把不等于写到where里会造成笛卡尔积,数据异常增大,速度会很慢。甚至会任务无法跑成功~ 根据mapjoin的计算原理,MapJoin会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配。这种情况下即使笛卡尔积也不会对任务运行速度造成太大的效率影响。 而且hive的where条件本身就是在map阶段进行的操作,所以在where里写入不等值比对的话,也不会造成额外负担。select /*+ MAPJOIN(a) */a.start_level, b.*from dim_level ajoin (select * from test) bwhere b.xx>=a.start_level and b.xx
3) MAPJOIN 结合 UNIONALL
原始sql:select a.*,coalesce(c.categoryid,’NA’) as app_categoryfrom (select * from t_aa_pvid_ctr_hour_js_mes1) aleft outer join(select * fromt_qd_cmfu_book_info_mes) con a.app_id=c.book_id;
速度很慢,老办法,先查下数据分布:
select *from(selectapp_id,count(1) cntfromt_aa_pvid_ctr_hour_js_mes1group by app_id) torder by cnt DESClimit 50;
数据分布如下:
NA 6173701292 1182933141 40673814d 20151236b 1846306s 11242465 6752408 6422316 611104t 5969734 5794733 4895167 4759999 373395107580 10508
我们可以看到除了NA是有问题的异常值,还有appid=1~9的数据也很多,而这些数据是可以关联到的,所以这里不能简单的随机函数了。而t_qd_cmfu_book_info_mes这张app库表,又有几百万数据,太大以致不能放入内存使用mapjoin。
解决方法:首先将appid=NA和1到9的数据存入一组,并使用mapjoin与维表(维表也限定appid=1~9,这样内存就放得下了)关联,而除此之外的数据存入另一组,使用普通的join,最后使用union all 放到一起。
select a.*,coalesce(c.categoryid,’NA’) as app_categoryfrom --if app_id isnot number value or <=9,then not join(select * fromt_aa_pvid_ctr_hour_js_mes1where cast(app_id asint)>9) aleft outer join(select * fromt_qd_cmfu_book_info_meswhere cast(book_id asint)>9) con a.app_id=c.book_idunion allselect /*+ MAPJOIN(c)*/a.*,coalesce(c.categoryid,’NA’) as app_categoryfrom –if app_id<=9,use map join(select * fromt_aa_pvid_ctr_hour_js_mes1where coalesce(cast(app_id as int),-999)<=9) aleft outer join(select * fromt_qd_cmfu_book_info_meswhere cast(book_id asint)<=9) c--if app_id is notnumber value,then not joinon a.app_id=c.book_id
设置:
当然也可以让hive自动识别,把join变成合适的Map Join如下所示
注:当设置为true的时候,hive会自动获取两张表的数据,判定哪个是小表,然后放在内存中set hive.auto.convert.join=true;select count(*) from store_sales join time_dim on (ss_sold_time_sk = t_time_sk)
SMB(Sort-Merge-Buket) Join
对于每一个表(table)或者分区, Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。Hive也是 针对某一列进行桶的组织。Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。 把表(或者分区)组织成桶(Bucket)有两个理由: (1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。 (2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。
场景:
大表对小表应该使用MapJoin,但是如果是大表对大表,如果进行shuffle,那就要人命了啊,第一个慢不用说,第二个容易出异常,既然是两个表进行join,肯定有相同的字段吧。
tb_a - 5亿(按排序分成五份,每份1亿放在指定的数值范围内,类似于分区表)
a_id 100001 ~ 110000 - bucket-01-a -1亿 110001 ~ 120000 120001 ~ 130000 130001 ~ 140000 140001 ~ 150000tb_b - 5亿(同上,同一个桶只能和对应的桶内数据做join)
b_id 100001 ~ 110000 - bucket-01-b -1亿 110001 ~ 120000 120001 ~ 130000 130001 ~ 140000 140001 ~ 150000注:实际生产环境中,一天的数据可能有50G(举例子可以把数据弄大点,比如说10亿分成1000个bucket)。
原理:
在运行SMB Join的时候会重新创建两张表,当然这是在后台默认做的,不需要用户主动去创建,如下所示:
ps:
1.两个表关联键为id,需要按id分桶并且做排序,小表的分桶数是大表分桶数的倍数。 2.对于map端连接的情况,两个表以相同方式划分桶。处理左边表内某个桶的 mapper知道右边表内相匹配的行在对应的桶内。因此,mapper只需要获取那个桶 (这只是右边表内存储数据的一小部分)即可进行连接。这一优化方法并不一定要求 两个表必须桶的个数相同,两个表的桶个数是倍数关系也可以 3.桶中的数据可以根据一个或多个列另外进行排序。由于这样对每个桶的连接变成了高效的归并排序(merge-sort), 因此可以进一步提升map端连接的效率启用桶表
set hive.enforce.bucketing = true;
设置(默认是false):
set hive.auto.convert.sortmerge.join=trueset hive.optimize.bucketmapjoin=true;set hive.optimize.bucketmapjoin.sortedmerge=true;
set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
ps:此时的状况是Bucket columns==Join Columns==Sort Columns,完全具备具备使用Sort Merge Bucket Map Join的条件。
查询
select /*+ mapjoin(b) */ count(1) from lxw_test1 a join lxw_test2 b on a.id = b.id
测试结果:
包括insert数据,采用Sort Merge Bucket Map Join的方式耗时10分钟左右。
如果这两个表做普通的join,耗时1个多小时,还跑不完,最后只得Kill掉了!
总结:
其实在写程序的时候,我们就可以知道哪些是大表哪些是小表,注意调优。