发生倾斜的根本原因在于,shuffle之后,key的分布不均匀,使得大量的key集中在某个reduce节点,导致此节点过于“忙碌”,在其他节点都处理完之后,任务的结整需要等待此节点处理完,使得整个任务被此节点堵塞,要解决此问题,主要可以分为两大块:一是尽量不shuffle;二是shuffle之后,在reduce节点上的key分布尽量均匀。
select key, count(*) cnt
from B b
group by key
order by cnt desc;
如果某个key的cnt比较大,比如排在第1的key,cnt占了一大半,那使用这个key去join,极有可能发生数据倾斜。
像图中,排在第1的key,占据了4161w条记录,占表总记录数的一半,如果使用这个key去join,运行时肯定倾斜。
如果发现,某个任务运行时间比较长,可以看下执行日志,如果运行时reduce阶段一直在99%,则极可能是发生了数据倾斜,如下图:
key相同的太集中,导致倾斜
key的哈希结果,或分区函数的结果,导致key分布集中
只要有shuffle,就有可能产生数据倾斜,解决数据倾斜的总体思路是:
具体的方法有以下:MapJoin,手工分割,添加随机前缀,使用列桶表等。
适应场景:Join的两个表中,有一个表比较小,小到可以完全放到分布式缓存中,默认的大小是25M。
配置方法(亲测有效):
set hive.auto.convert.join = true;
set hive.mapjoin.smalltable.filesize=25000000;
优点:运行时转换成mapjoin,无reduce阶段,运行时间极短
缺点:适用场景有限,需要占用分布式缓存
mapJoin原理
适应场景:当我们知道某些少量极个别的key 去join时会发生倾斜,我们可以手工将其分开执行,比如key=A时会发生倾斜,我们此时可以将所有的key分成两块来执行,一块是key=A,另一块是key<>A,如下:
实践代码(亲测有效)
select t1.*
from t1 join t2 on t1.key=t2.key
拆成以下SQL:
select t1.*
from t1 join t2 on t1.key=t2.key
where t1.key=A
union all
select t1.*
from t1 join t2 on t1.key=t2.key
where t1.key<>A
缺点:场景有限
这种场景需要注意一下:如果倾斜的key,除了key=A外,还有其他key时也会发生倾斜时,并不一定适合去拆分。
select a.*
from a
left join (
select concat(c.rand_num,'_',d.key) as key from(
select rand_num from dual LATERAL VIEW explode(array(0,1,2,3,4,5,6,7,8,9)) rand_num_list as rand_num
)c join d
)b on concat(cast(9*rand() as int), '_', a.key) =b.key
where a.ds='2019-09-05'
Skewed Table vs. List Bucketing Table
create table T (c1 string, c2 string) skewed by (c1) on ('x1')
STORED AS DIRECTORIES
,例如:create table T (c1 string, c2 string) skewed by (c1) on ('x1') stored as directories;
create table T (c1 string, c2 string, c3 string) skewed by (c1, c2) on (('x1', 'x2'), ('y1', 'y2')) stored as directories;
适用场景:
不适用场景:
Skewed table 使用方法 :
set hive.optimize.skewjoin.compiletime =true;
set hive.optimize.skewjoin =true;
set hive.exec.reducers.bytes.per.reducer = 100000000;
配置:在Hive0.14.0版本之前,此配置默认为1G(1,000,000,000),在版本0.14.0之后,默认为256MB
优点:reduce个数增多,可以提高速度
缺点:但这个阈值不太好控制,还是容易产生倾斜。
对于spark作业
对于spark作业,可以采用spark自适应特性来缓解数据倾斜问题,配置如下:
属性名称 | 默认值 | 备注 |
---|---|---|
spark.sql.adaptive.enabled | false | 自适应执行框架的开关 |
spark.sql.adaptive.shuffle.targetPostShuffleInputSize | 67108864 | 动态调整reduce个数的 partition 大小依据,如设置64MB,则reduce 阶段每个task最少处理 64MB的数据。 |
spark.sql.adaptive.shuffle.targetPostShuffleRowCount | 20000000 | 动态调整reduce个数的partition条数依据,如设置20000000则reduce阶段每个task最少处理 20000000条的数据。 |
可以通过配置调整reduce阶段每个task处理的数据量的大小或处理的记录条数,经缓解数据倾斜问题
以下两种方式都试过,但没感觉到提速。
set hive.optimize.skewjoin=true;
set hive.skewjoin.key=100000; --如果在同一个join operator出现超过此阈值的相同的key,则认为此key为倾斜key
set mapred.reduce.tasks=800;
http://www.jasongj.com/spark/skew/
https://cwiki.apache.org/confluence/display/Hive/Skewed+Join+Optimization
https://cwiki.apache.org/confluence/display/Hive/ListBucketing
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+JoinOptimization#LanguageManualJoinOptimization-PriorSupportforMAPJOIN