Hive数据倾斜优化总结
Hive数据倾斜优化分为配置优化和SQL优化
优先原则:
- 数据不怕多,避免倾斜。
- 减少Job数,Job的启动关闭是很耗资源。
- 尽量不用Count(distinct) ,效率很低
- 定期合并hdfs上的小文件
- 设置合理的MR任务数,不是越多越好
- 保证全局最优,局部最优不一定是最优
SQL优化
1.小表join大表
使用MapJoin,让小表进入内存,减少磁盘读写
INSERT OVERWRITE TABLE pv_users
SELECT /*+ MAPJOIN(pv) */ pv.pageid, u.age
FROM page_view pv
JOIN user u ON (pv.userid = u.userid);
2.大表join大表
处理空值或者某一个倾斜的key
通用方法1:
给它膨胀N倍后加上随机数,在连表的时候保留原有的条件的同时加一个随机数的连接条件,这样可以分散key
Select * from log a
Left outer join (select /*+mapjoin(e)*/
memberid, number
From members d
Join num e
) b
On a.memberid= b.memberid
And mod(a.pvtime,30)+1=mber;
上面代码中在连表时候,使用数字表与key少的那张表进行笛卡尔积膨胀了n倍,并且这里使用MapJoin将自定义的小表加载进入内存,然后在原来的连表条件上加上了数字连接的条件,这样原来同一个的key就会被分散为n份到reduce上,这样就避免特别多的key在一个reduce上。
这里的取余函数尤为重要,否则就会出现最后的结果也膨胀了n倍,这样的结果不是我们想要看到的
处理空值
如果你的倾斜的key你发现是null,那就使用下面这种方法,让null值不参与连接
select *
from log a
left outer join bmw_users b
on case when a.user_id is null then concat(‘dp_hive’,rand() ) else a.user_id end = b.user_id;
生成一个随机数给null然后避免它参加连表,只是使用leftjoin将它留下来就可以
当然你也可以单独筛选出null值来unio all,据说那样的效率不如上面的效率
3.减少job数
怎么减少job数呢?首先你的sql要尽可能写的简单,你可以数据分层,一层层向上聚合,那你的每个脚本的job数就减少,然后就是里union all可以合并job数的特性
select * from effect a
join (
select auction_id as auction_id from auctions
union all
select auction_id as auction_id from auctions2
) b
on a.auction_id = b.auction_id;
上面的内层sql中因为auction_id和auction_string_id数据类型不一样,如果你单独去连接effect表后再去合并那就会有3个job数,但是这样先合并就会减少一个job.如果他们是同一张表的id,只是类型不一样,我觉得直接转换类型即可,没必要这样做。网上的人的例子两个id是一张表的,只是类型不一致,我觉得没必要用这种方法
union all 也是有限制的在某些情况是不能合并job的
1.只能union all 非嵌套查询
2.查询中不能join,group by ,count(distinct ) ,当然这些你都是可以想办法搬动的,并不是遇到这些情况就完全不能使用
4.小表不大也不小
如果你的小表只是相对于的大表是小表,相对于你的集群资源还是一个大表,这时候有以上几条
1.如果聚合结果不影响最后的结果,你可以先聚合两张表以减少数量
2.如果你的大表虽然大,但是其实它最后连接的数量并不多,你可以使用小面的代码,先处理大表。
select /*+mapjoin(x)*/* from log a
left outer join (
select /*+mapjoin(c)*/d.*
from ( select distinct memberid from log ) c
join members d
on c.memberid = d.memberid
) x
on a.memberid = b.memberid;
上面代码中,选对日志表中memberid 去重,然后mapjoin members,这样就减少了你直接连接log的数量。这种情况是因为log表示是每个用户的操作日志,那么一个用户操作10次,但是还是一个用户id,这样做就在内层的连接中减少10倍。但是这也是业务上问题,必须你的两张表满足这样的条件,如果不确定的情况下最好用通用方法解决
5.减少oderby的使用,使用ditribute by + sort by
order by是一个reduce进行排序聚合,但是sort by是使用多个reduce分别排序,最后再进行的一个归并排序。
使用sort by的注意事项:
1.首先设置 task,不能为一个
2.使用sort by 你可以指定执行的reduce 个数 (set mapred.reduce.tasks=
3.ditribute by的字段需要与sort by后面的一致
如下面两种,第一种的结果只是部分有序,第二种
第一种:
SELECT `date`, mediaid, slotid, SUM(mediaclickcost)
FROM clickcube_mid
GROUP BY `date`, mediaid, slotid
DISTRIBUTE BY `date`
SORT BY `date` DESC, mediaid, slotid;
第二种能达到order by的效果
SELECT `date`, mediaid, slotid, SUM(mediaclickcost)
FROM clickcube_mid GROUP BY `date`, mediaid, slotid
CLUSTER BY `date` DESC, mediaid, slotid;
6.数据分层的思想
不要想着一步就到位,那样你的sql会非常复杂而且也会效率非常低。学会在表的分层和sql的复杂程度中平衡。一维的去分层只会让你数据架构复杂,学好建模很重要
配置优化
配置 | 原因 | |
---|---|---|
Fetch抓取 | 简单的select防止它走MR直接读取文件,设置"more" | |
本地模式 | 如果数据少可以开启,然后在单台机器上运行 | |
MapJoin | 小表加载进入内存减少读写磁盘 | |
Map端聚合 | 有些聚合操作可以在MAP端完成 | |
动态分区 | 动态插入分区,使用查询的字段自动进行分区 | |
并行执行 | 一个查询的多个阶段,不交叉的部分进行并行 | |
严格模式 | 防止一些效率很低的写法 | |
JVM重用 | 减少重启JVM的资源消耗 | |
推测执行 | 推测出执行很久的任务,开启一个备份任务,取先执行完的结果 |
设置压缩方式与存储格式
比如:数据的存储格式 orc 或者 parquet
压缩方式 snappy