Hive优化-大表join大表优化
Hive优化-大表join大表优化
5、大表join大表优化
如果Hive优化实战2中mapjoin中小表dim_seller很大呢?比如超过了1GB大小?这种就是大表join大表的问题。首先引入一个具体的问题场景,然后基于此介绍各自优化方案。
5.1、问题场景
问题场景如下:
A表为一个汇总表,汇总的是卖家买家最近N天交易汇总信息,即对于每个卖家最近N天,其每个买家共成交了多少单,总金额是多少,假设N取90天,汇总值仅取成交单数。
A表的字段有:buyer_id、seller_id、pay_cnt_90day。
B表为卖家基本信息表,其字段有seller_id、sale_level,其中sale_levels是卖家的一个分层评级信息,比如吧卖家分为6个级别:S0、S1、S2、S3、S4和S5。
要获得的结果是每个买家在各个级别的卖家的成交比例信息,比如:
某买家:S0:10%;S1:20%;S2:20%;S3:10%;S4:20%;S5:10%。
正如mapjoin中的例子一样,第一反应是直接join两表并统计:
select
m.buyer_id,
sum(pay_cnt_90day) as pay_cnt_90day,
sum(case when m.sale_level = 0 then pay_cnt_90day end) as pay_cnt_90day_s0,
sum(case when m.sale_level = 1 then pay_cnt_90day end) as pay_cnt_90day_s1,
sum(case when m.sale_level = 2 then pay_cnt_90day end) as pay_cnt_90day_s2,
sum(case when m.sale_level = 3 then pay_cnt_90day end) as pay_cnt_90day_s3,
sum(case when m.sale_level = 4 then pay_cnt_90day end) as pay_cnt_90day_s4,
sum(case when m.sale_level = 5 then pay_cnt_90day end) as pay_cnt_90day_s5
from (
select a.buer_id, a.seller_id, b.sale_level, a.pay_cnt_90day
from ( select buyer_id, seller_id, pay_cnt_90day from table_A) a
join
(select seller_id, sale_level from table_B) b
on a.seller_id = b.seller_id
) m
group by m.buyer_id
但是此SQL会引起数据倾斜,原因在于卖家的二八准则,某些卖家90天内会有几百万甚至上千万的买家,但是大部分的卖家90天内买家的数目并不多,join table_A和table_B的时候,
ODPS会按照seller_id进行分发,table_A的大卖家引起了数据倾斜。
但是数据本身无法用mapjoin table_B解决,因为卖家超过千万条,文件大小有几个GB,超过了1GB的限制。
5.2、优化方案1:转为mapjoin
一个很正常的想法是,尽管B表无法直接mapjoin, 但是是否可以间接mapjoin它呢?
实际上此思路有两种途径:限制行和限制列。
限制行的思路是不需要join B全表,而只需要join其在A表中存在的,对于本问题场景,就是过滤掉90天内没有成交的卖家。
限制列的思路是只取需要的字段。
加上如上的限制后,检查过滤后的B表是否满足了Hive mapjoin的条件,如果能满足,那么添加过滤条件生成一个临时B表,然后mapjoin该表即可。采用此思路的语句如下:
select
m.buyer_id,
sum(pay_cnt_90day) as pay_cnt_90day,
sum(case when m.sale_level = 0 then pay_cnt_90day end) as pay_cnt_90day_s0,
sum(case when m.sale_level = 1 then pay_cnt_90day end) as pay_cnt_90day_s1,
sum(case when m.sale_level = 2 then pay_cnt_90day end) as pay_cnt_90day_s2,
sum(case when m.sale_level = 3 then pay_cnt_90day end) as pay_cnt_90day_s3,
sum(case when m.sale_level = 4 then pay_cnt_90day end) as pay_cnt_90day_s4,
sum(case when m.sale_level = 5 then pay_cnt_90day end) as pay_cnt_90day_s5
from (
select /*+mapjoin(b)*/
a.buer_id, a.seller_id, b.sale_level, a.pay_cnt_90day
from ( select buyer_id, seller_id, pay_cnt_90day from table_A) a
join
(
select seller_id, sale_level from table_B b0
join
(select seller_id from table_A group by seller_id) a0
on b0.seller_id = a0.selller_id
) b
on a.seller_id = b.seller_id
) m
group by m.buyer_id
此方案在一些情况可以起作用,但是很多时候还是无法解决上述问题,因为大部分卖家尽管90天内买家不多,但还是有一些的,过滤后的B表仍然很多。
5.3、优化方案2:join时用case when语句
此种解决方案应用场景是:倾斜的值是明确的而且数量很少,比如null值引起的倾斜。其核心是将这些引起倾斜的值随机分发到Reduce,其主要核心逻辑在于join时对这些特殊值concat随机数,
从而达到随机分发的目的。此方案的核心逻辑如下:
select a.user_id, a.order_id, b.user_id
from table_a a join table_b b
on (case when a.user_is is null then concat('hive', rand()) else a.user_id end) = b.user_id
Hive 已对此进行了优化,只需要设置参数skewinfo和skewjoin参数,不修改SQL代码,例如,由于table_B的值“0” 和“1”引起了倾斜,值需要做如下设置:
set hive.optimize.skewinfo=table_B:(selleer_id) [ ( "0") ("1") ) ]
set hive.optimize.skewjoin = true;
但是方案2因为无法解决本问题场景的倾斜问题,因为倾斜的卖家大量存在而且动态变化。
5.4 、优化方案3:倍数B表,再取模join
1、通用方案
此方案的思路是建立一个numbers表,其值只有一列int 行,比如从1到10(具体值可根据倾斜程度确定),然后放大B表10倍,再取模join。代码如下:
select
m.buyer_id,
sum(pay_cnt_90day) as pay_cnt_90day,
sum(case when m.sale_level = 0 then pay_cnt_90day end) as pay_cnt_90day_s0,
sum(case when m.sale_level = 1 then pay_cnt_90day end) as pay_cnt_90day_s1,
sum(case when m.sale_level = 2 then pay_cnt_90day end) as pay_cnt_90day_s2,
sum(case when m.sale_level = 3 then pay_cnt_90day end) as pay_cnt_90day_s3,
sum(case when m.sale_level = 4 then pay_cnt_90day end) as pay_cnt_90day_s4,
sum(case when m.sale_level = 5 then pay_cnt_90day end) as pay_cnt_90day_s5
from (
select a.buer_id, a.seller_id, b.sale_level, a.pay_cnt_90day
from ( select buyer_id, seller_id, pay_cnt_90day from table_A) a
join
(
select /*+mapjoin(members)*/
seller_id, sale_level ,member
from table_B
join members
) b
on a.seller_id = b.seller_id
and mod(a.pay_cnt_90day,10)+1 = b.number
) m
group by m.buyer_id
此思路的核心在于,既然按照seller_id分发会倾斜,那么再人工增加一列进行分发,这样之前倾斜的值的倾斜程度会减少到原来的1/10,可以通过配置numbers表改放大倍数来降低倾斜程度,
但这样做的一个弊端是B表也会膨胀N倍。
2、专用方案
通用方案的思路把B表的每条数据都放大了相同的倍数,实际上这是不需要的,只需要把大卖家放大倍数即可:需要首先知道大卖家的名单,即先建立一个临时表动态存放每天最新的大卖家(
比如dim_big_seller),同时此表的大卖家要膨胀预先设定的倍数(1000倍)。
在A表和B表分别新建一个join列,其逻辑为:如果是大卖家,那么concat一个随机分配正整数(0到预定义的倍数之间,本例为0~1000);如果不是,保持不变。具体代码如下:
select
m.buyer_id,
sum(pay_cnt_90day) as pay_cnt_90day,
sum(case when m.sale_level = 0 then pay_cnt_90day end) as pay_cnt_90day_s0,
sum(case when m.sale_level = 1 then pay_cnt_90day end) as pay_cnt_90day_s1,
sum(case when m.sale_level = 2 then pay_cnt_90day end) as pay_cnt_90day_s2,
sum(case when m.sale_level = 3 then pay_cnt_90day end) as pay_cnt_90day_s3,
sum(case when m.sale_level = 4 then pay_cnt_90day end) as pay_cnt_90day_s4,
sum(case when m.sale_level = 5 then pay_cnt_90day end) as pay_cnt_90day_s5
from (
select a.buer_id, a.seller_id, b.sale_level, a.pay_cnt_90day
from (
select /*+mapjoin(big)*/
buyer_id, seller_id, pay_cnt_90day,
if(big.seller_id is not null, concat( table_A.seller_id, 'rnd', cast( rand() * 1000 as bigint ), table_A.seller_id) as seller_id_joinkey
from table_A
left outer join
--big表seller_id有重复,请注意一定要group by 后再join,保证table_A的行数保持不变
(select seller_id from dim_big_seller group by seller_id)big
on table_A.seller_id = big.seller_id
) a
join
(
select /*+mapjoin(big)*/
seller_id, sale_level ,
--big表的seller_id_joinkey生成逻辑和上面的生成逻辑一样
coalesce(seller_id_joinkey,table_B.seller_id) as seller_id_joinkey
from table_B
left out join
--table_B表join大卖家表后大卖家行数扩大1000倍,其它卖家行数保持不变
(select seller_id, seller_id_joinkey from dim_big_seller) big
on table_B.seller_id= big.seller_id
) b
on a.seller_id_joinkey= b.seller_id_joinkey
and mod(a.pay_cnt_90day,10)+1 = b.number
) m
group by m.buyer_id
相比通用方案,专用方案的运行效率明细好了许多,因为只是将B表中大卖家的行数放大了1000倍,其它卖家的行数保持不变,但同时代码复杂了很多,而且必须首先建立大数据表。
5.5 、方案4:动态一分为二
实际上方案2和3都用了一分为二的思想,但是都不彻底,对于mapjoin不能解决的问题,终极解决方案是动态一分为二,即对倾斜的键值和不倾斜的键值分开处理,不倾斜的正常join即可,倾斜的把他们找出来做mapjoin,最后union all其结果即可。
但是此种解决方案比较麻烦,代码复杂而且需要一个临时表存放倾斜的键值。代码如下:
--由于数据倾斜,先找出90天买家超过10000的卖家
insert overwrite table temp_table_B
select
m.seller_id, n.sale_level
from (
select seller_id
from (
select seller_id,count(buyer_id) as byr_cnt
from table_A
group by seller_id
) a
where a.byr_cnt >10000
) m
left join
(
select seller_id, sale_level from table_B
) n
on m.seller_id = n.seller_id;
--对于90天买家超过10000的卖家直接mapjoin,对其它卖家直接正常join即可。
select
m.buyer_id,
sum(pay_cnt_90day) as pay_cnt_90day,
sum(case when m.sale_level = 0 then pay_cnt_90day end) as pay_cnt_90day_s0,
sum(case when m.sale_level = 1 then pay_cnt_90day end) as pay_cnt_90day_s1,
sum(case when m.sale_level = 2 then pay_cnt_90day end) as pay_cnt_90day_s2,
sum(case when m.sale_level = 3 then pay_cnt_90day end) as pay_cnt_90day_s3,
sum(case when m.sale_level = 4 then pay_cnt_90day end) as pay_cnt_90day_s4,
sum(case when m.sale_level = 5 then pay_cnt_90day end) as pay_cnt_90day_s5
from (
select a.buer_id, a.seller_id, b.sale_level, a.pay_cnt_90day
from ( select buyer_id, seller_id, pay_cnt_90day from table_A) a
join
(
select seller_id, a.sale_level
from table_A a
left join temp_table_B b
on a.seller_id = b.seller_id
where b.seller_id is not null
) b
on a.seller_id = b.seller_id
union all
select /*+mapjoin(b)*/
a.buer_id, a.seller_id, b.sale_level, a.pay_cnt_90day
from (
select buyer_id, seller_id, pay_cnt_90day
from table_A
) a
join
(
select seller_id, sale_level from table_B
) b
on a.seller_id = b.seller_id
) m group by m.buyer_id
) m
group by m.buyer_id
总结:方案1、2以及方案3中的同用方案不能保证解决大表join大表问题,因为它们都存在种种不同的限制和特定使用场景。
而方案3的专用方案和方案4是推荐的优化方案,但是它们都需要新建一个临时表来存储每日动态变化的大卖家。相对方案4来说,方案3的专用方案不需要对代码框架进行修改,但是B表会被放大,所以一定要是是维度表,不然统计结果会是错误的。方案4最通用,自由度最高,但是对代码的更改也最大,甚至修改更难代码框架,可以作为终极方案使用。
参考资料:《离线和实时大数据开发实战》
Hive优化-大表join大表优化的更多相关文章
- 大数据开发实战:Hive优化实战3-大表join大表优化
5.大表join大表优化 如果Hive优化实战2中mapjoin中小表dim_seller很大呢?比如超过了1GB大小?这种就是大表join大表的问题.首先引入一个具体的问题场景,然后基于此介绍各自优 ...
- 大数据开发实战:Hive优化实战2-大表join小表优化
4.大表join小表优化 和join相关的优化主要分为mapjoin可以解决的优化(即大表join小表)和mapjoin无法解决的优化(即大表join大表),前者相对容易解决,后者较难,比较麻烦. 首 ...
- 【Spark调优】大表join大表,少数key导致数据倾斜解决方案
[使用场景] 两个RDD进行join的时候,如果数据量都比较大,那么此时可以sample看下两个RDD中的key分布情况.如果出现数据倾斜,是因为其中某一个RDD中的少数几个key的数据量过大,而另一 ...
- 【Spark调优】小表join大表数据倾斜解决方案
[使用场景] 对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小(例如几百MB或者1~2GB),比较适用此方案. [解决方案] ...
- hive join 优化 --小表join大表
1.小.大表 join 在小表和大表进行join时,将小表放在前边,效率会高.hive会将小表进行缓存. 2.mapjoin 使用mapjoin将小表放入内存,在map端和大表逐一匹配.从而省去red ...
- SQL记录-小表join大表查询例子
- (转)MapReduce 中的两表 join 几种方案简介
1. 概述 在传统数据库(如:MYSQL)中,JOIN操作是非常常见且非常耗时的.而在HADOOP中进行JOIN操作,同样常见且耗时,由于Hadoop的独特设计思想,当进行JOIN操作时,有一些特殊的 ...
- MapReduce 中的两表 join 几种方案简介
转自:http://my.oschina.net/leejun2005/blog/95186 MapSideJoin例子:http://my.oschina.net/leejun2005/blog/1 ...
- 35 | join语句怎么优化?
在上一篇文章中,我和你介绍了 join 语句的两种算法,分别是 Index Nested-Loop Join(NLJ) 和 Block Nested-Loop Join(BNL). 我们发现在使用 N ...
随机推荐
- mysql中查看表结构的sql语句
mysql查看表结构命令,如下: desc 表名;show columns from 表名;describe 表名;show create table 表名; use information_sche ...
- Web测试项目计划与安排
本次Web测试项目实践的需求如下: 1 选中某一款产品(暂且选择博客园和CSDN进行横向比较),对被测产品进行评测: 2 进行用户调研: 3 对产品进行定量的评价: 4 对这个产品进行分析: 5 例会 ...
- Ajax之XMLHttpRequest
XMLHttpRequest对象 XMLHttpRequest 提供客户端同http服务器通讯的协议 一:创建 IE : http_request = new ActiveXObject(" ...
- shell 编程 变量
转自:http://blog.csdn.net/qq504196282/article/details/52994249 shell之变量和引用 分类:SHELL编程基础 (470) (0) 举报 ...
- 设计模式16:Mediator 中介者模式(行为型模式)
Mediator 中介者模式(行为型模式) 依赖关系的转化 动机(Motivation) 在软件构建过程中,经常出现多个对象互相关联交互的情况,对象之间经常会维持一种复杂的应用关系,如果遇到一些需求的 ...
- 昨天发现的nginx默认不支持下划线引起angular-phonecat项目部分文件404问题
今天发现原来确实是不存在这个文件... 因为我的Ubuntu的git没有安装,所以导致bower install时没有拉下来依赖包,因为bower是使用的git拉的包. 但是同时也搜到了nginx不支 ...
- Java50道经典习题-程序2 输出素数
题目:判断101-200之间有多少个素数,并输出所有素数 分析:判断素数的方法:用一个数分别去除2到(这个数-1)的数,如果能被整除,则表明此数不是素数,反之是素数. public class Pro ...
- ANE-IOS与AS的互通
从AS调用IOS的函数与传参数 extContex是ExtensionContext的实例,通过call调用.第一个参数是IOS对应的函数,之后所有的参数会变成一个数组传入IOS中.也就是说函数名称后 ...
- 手把手带你打造一个 Android 热修复框架
本文来自网易云社区 作者:王晨彦 Application 处理 上面我们已经对所有 class 文件插入了 Hack 的引用,而插入 dex 是在 Application 中,Application ...
- 数据库客户端工具Oracle SQL Developer
Oracle SQL Developer是Oracle官方提供的数据库连接工具.不仅可以连接自己的数据库(Oracle),而且还可以连接多种其他的数据库(比如:Access.MySQL.SQL Ser ...