Hive数据倾斜总结

发生倾斜的根本原因在于,shuffle之后,key的分布不均匀,使得大量的key集中在某个reduce节点,导致此节点过于“忙碌”,在其他节点都处理完之后,任务的结整需要等待此节点处理完,使得整个任务被此节点堵塞,要解决此问题,主要可以分为两大块:一是尽量不shuffle;二是shuffle之后,在reduce节点上的key分布尽量均匀。

倾斜探查

join key倾斜

select key, count(*) cnt 
from B b 
group by key
order by cnt desc;

如果某个key的cnt比较大,比如排在第1的key,cnt占了一大半,那使用这个key去join,极有可能发生数据倾斜。

hive left jon 大小写 hive中left join数据倾斜_数据倾斜


像图中,排在第1的key,占据了4161w条记录,占表总记录数的一半,如果使用这个key去join,运行时肯定倾斜。

查看日志

如果发现,某个任务运行时间比较长,可以看下执行日志,如果运行时reduce阶段一直在99%,则极可能是发生了数据倾斜,如下图:

hive left jon 大小写 hive中left join数据倾斜_hive优化_02

倾斜原因

  • key相同的太集中,导致倾斜
  • key的哈希结果,或分区函数的结果,导致key分布集中

解决方案

只要有shuffle,就有可能产生数据倾斜,解决数据倾斜的总体思路是:

  • 能在map端处理的,不留到reduce端(当然,这不仅是解决数据倾斜的思路,这个适用所有的优化):
  • 根据谓词下推规则,尽早过滤数据;
  • 使用map Join
  • 将key值尽量分散。
  • 使用随机数打散,但要考虑不能影响结果。
  • 提高并行度

具体的方法有以下:MapJoin,手工分割,添加随机前缀,使用列桶表等。

1. mapjoin

  • 适应场景:Join的两个表中,有一个表比较小,小到可以完全放到分布式缓存中,默认的大小是25M。
  • 配置方法(亲测有效):
set hive.auto.convert.join = true;
set hive.mapjoin.smalltable.filesize=25000000;
  • 优点:运行时转换成mapjoin,无reduce阶段,运行时间极短
  • 缺点:适用场景有限,需要占用分布式缓存

mapJoin原理

  • Local work:
  • read records via standard table scan (including filters and projections) from source on local machine
  • build hashtable in memory
  • write hashtable to local disk
  • upload hashtable to dfs
  • add hashtable to distributed cache
  • Map task
  • read hashtable from local disk (distributed cache) into memory
  • match records’ keys against hashtable
  • combine matches and write to output
  • No reduce task

2. 手工分割

  • 适应场景:当我们知道某些少量极个别的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时也会发生倾斜时,并不一定适合去拆分。

3. 大表添加N种随机前缀,小表膨胀N倍数据

  • 适用场景:小表不是很小,不太方便使用mapjoin。
  • 解决方法:对大表的key添加N种随机前缀,对小表和N种随机前缀做笛卡尔积,使得大表里新生成的key在小表里都有可能正确地找到相应的key,解决思路可参考以下图:
  • 实践代码(亲测有效),以下代码是N的取值为10
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'
  • 优点:可适当降低倾斜程度。
  • 缺点:N的取值不太好判断,需要多次实验,而且数据膨胀后,会增加资源消耗。

4. 使用Skewed Table或List Bucketing Table

  • Skewed Table vs. List Bucketing Table
  • Skewed Table是倾斜表,元数据存储了倾斜key的信息,例如:
create table T (c1 string, c2 string) skewed by (c1) on ('x1')
  • List Bucketing Table是一种特殊的倾斜表,数据存储的时候会带有倾斜key的子目录,创建时需要指定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 不会创建子目录,但list bucketing table会。
  • 适用场景:
  • 每个分区中,倾斜key的数据占了绝大部分,比如表中的key有10个,但key=key1的数据占了总数据量的60%;
  • 每个分区中,倾斜的key比较少,因为太多容易给元数据造成压力。
  • 不适用场景:
  • 如果倾斜的key的数量比较多,会给元数据存储造成压力
  • Skewed table 使用方法 :
set hive.optimize.skewjoin.compiletime =true;
set hive.optimize.skewjoin =true;
  • List Bucketing Table不指定配置参数,直接Join,就能生效

5. 配置每个reduce节点处理的数据量

  • 对于hive作业配置如下:
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;
  • 优点:reduce个数增多,可以提高速度
  • 缺点:但这个阈值不太好控制,还是容易产生倾斜。

参考文献:

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