Hive数据倾斜调优
1.数据倾斜分哪几类以及如何优化?
开发人员首先要确认几点:
-
需要计算的指标真的需要从数据仓库的公共明细层来自行汇总吗?数据团队开发的公共汇总层是否可以满足其要求了?
-
真的需要扫描这么多分区吗?能扫描一周的就不扫描一年的。
-
尽量不要使用select * from table这样的词语,能指定哪一列就用那一列,尽量添加过滤条件。
-
输入文件不要大量小文件,小文件可以先合并成大文件。
如果以上问题都排完雷了,数据倾斜问题依然存在,那我们继续往下看~
2.数据倾斜的原因
2.1操作
关键词 |
情形 |
后果 |
Join |
其中一个表较小, 但是key集中 |
分发到某一个或几个Reduce上的数据远高于平均值 |
大表与大表,但是分桶的判断字段0值或空值过多 |
这些空值都由一个reduce处理,灰常慢 |
|
group by |
group by 维度过小, 某值的数量过多 |
处理某值的reduce灰常耗时 |
Count Distinct |
某特殊值过多 |
处理此特殊值的reduce耗时 |
2.2原因
- key分布不均匀
- 业务数据本身的特性
- 建表时考虑不周
- 某些SQL语句本身就有数据倾斜
2.3表现
任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。
单一reduce的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。 最长时长远大于平均时长。
3.解决方案
3.1和join无关的优化场景
3.1.1group by引起的倾斜优化
举例:比如按照经销商的供应商来做group by,那么有些大经销商就有很多订单,而一些小经销商只有几个订单,那么分配给发大经销商的reduce task就有许多订单,所以造成数据倾斜。
解决:参数设置
- Map 端部分聚合,相当于Combiner
hive.map.aggr = true
- 有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。
hive.groupby.skewindata=true
3.1.2count distinct的优化
在开发过程中,要小心使用count distinct。
举例:
select count(distinct user) from table
解决:SQL优化
由于必须要去重了,所以hive会把所有的map阶段输出都放在一个reduce上,造成性能问题,所以通过group by再count进行优化。
select count(*)from( select user from table group by user) tmp
3.2和join有关的优化场景
3.2.1大表join小表优化
举例:有两个表,一个是供应商,一个是订单,供应商的表不会很大,因为供应商就这么点儿人,而订单的表就很大了。两个表join一下,就是一个很经典的大表join小表问题。
解决:mapjoin hint的方式
select /*+mapjoin(b小表)*/
3.2.2大表join大表优化
举例:有两个表,A表表示买家卖家的交易汇总表,B表表示卖家的段位评级。想要获得卖家在各个级别卖家的成交比例。
如某个买家:金冠店:40%,皇冠店:30%,钻石店:20%,星级店:10%
这两个表都会很大,超过mapjoin1G的限制,所以需要找其他方法解决。
- 方案一:hive.optimize.skewjoin参数配置
如果大表和大表进行join操作,则可采用skewjoin
skewjoin原理
-
对于skewjoin.key,在执行job时,将它们存入临时的HDFS目录。其它数据正常执行
-
对倾斜数据开启map join操作,对非倾斜值采取普通join操作
-
将倾斜数据集和非倾斜数据及进行合并操作
该参数为在运行时动态指定数据进行skewjoin,一般和hive.skewjoin.key参数一起使用
set hive.optimize.skewjoin=true; set hive.skewjoin.key=100000;
以上参数表示当记录条数超过100000时采用skewjoin操作
- 方案二:mapjoin hint的方式
此思路有两种途径:限制行和列
限制行的思路是比如不需要join b表全部,只需要join a表中存在的就行,比如过滤掉90天内没交易过的卖家
限制列的思路是只取需要的字段
- 方案三:join时候用case when语句随机数
解决场景是倾斜的值明确且数量很少,如用null引起的值。
核心是将这些倾斜的值发放到随机的reduce,具体做法是在join时对这些特殊的值concat随机数,从而达到随即分发的目的。
on (case when a.user_id is null then concat('hive',rand()) else a.user_id end) = b.user_id
- 方案四:动态一分为二
最彻底的方式就是动态一分为二,将倾斜和不倾斜的值分开处理,如果不倾斜的直接正常join,如果倾斜的找出来做mapjoin,最后结果union all即可。
4.典型业务场景
4.1空值产生的数据倾斜
场景:如日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和 用户表中的user_id 关联,会碰到数据倾斜的问题。
解决方法1: user_id为空的不参与关联
select * from log a join users b on a.user_id is not null and a.user_id = b.user_id union all select * from log a where a.user_id is null;
解决方法2 :赋与空值分新的key值
select * from log a left outer join users b on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
结论:方法2比方法1效率更好,不但io少了,而且作业数也少了。解决方法1中 log读取两次,jobs是2。解决方法2 job数是1 。这个优化适合无效 id (比如 -99 , ’’, null 等) 产生的倾斜问题。把空值的 key 变成一个字符串加上随机数,就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。
4.2不同数据类型关联产生数据倾斜
场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类 型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分 配到一个Reducer中。
解决方法:把数字类型转换成字符串类型
select * from users a left outer join logs b on a.usr_id = cast(b.user_id as string)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY