上一篇已经从各方面总结了Hive的优化,按很多时候要解决数据倾斜才是优化的关键。
在MapReduce程序中,大量的相同key被partition分配到一个分区里,使这个节点承受着巨大的压力,而其他节点计算完毕后要一直等待这个忙碌的节点,这样一来也拖累了整体的计算时间,使数据的生产效率十分低下,总而言之这都是数据倾斜造成的。
造成数据倾斜的原因有很多,这里总结下以下几点原因:
- key分布不均匀
- 业务数据本身的特性
- 建表时考虑不周
- 某些SQL语句本身就有数据倾斜
数据倾斜一般可以分为三种:
- Mapper阶段数据倾斜
- Join阶段数据倾斜
- Reduce阶段数据倾斜
解决数据倾斜
1.Mapper阶段数据倾斜
可以修改读取数据的表的任务,比如对key进行随机化处理,最后插入数据时按照均衡的key值重新分布。也就是在最后加上distribute by…。
distribute by 的作用在于控制 map 中的输出在 reducer 中是如何进行划分的。使用distribute by 可以保证相同key的记录被划分到一个 Reduce 中。
如果Mapper的任务数比较少,比如200以内,可以考虑增加Mapper的任务数,从而减少单个任务的处理数据量和执行时间
2.Join阶段数据倾斜
常见的数据倾斜类型,可按照表的大小和join方式的不同对应有多种处理方式
上篇文章已经介绍过了,在这里就不过多叙述。
3.Reduce 阶段的数据倾斜
原因:
- Group by后面的字段值存在数据倾斜(但针对单个distinct比较有效,多个没效果)
- Distinct后面的字段加上group by后面的字段(如果有group by的话,没有就是distinct后面的字段)存在数据倾斜
- 动态分区导致数据倾斜
- 窗口函数中partition by后面的字段存在数据倾斜
解决group by 后的字段数据倾斜
set hive.groupby.skewindata = true 默认false
有数据倾斜的时候在group by之前增加一步distribute,把相同key的数据打散到多个instance上处理,从而达到负载均衡的目的。
解决同一个程序中有多个distinct(3个以上)
用打标+聚合的方式代替distinct的聚合
尽量减少程序中的distinct,其实去重运算是很慢的,尤其是多个count(distinct)的时候,你会发现为什么源表的数据量经过map后怎么变大了;
有多少个count(distinct)就会变大几倍,导致处理很慢。
利用窗口函数
可以减少1次join操作,从而提高计算性能
增加中间表减少任务数据处理量
把多个程序中类似的处理逻辑提炼到同一个任务中处理,生产中间表供后续任务共用,可以有效地减少后续任务的数据处理量
增加分区减少后续任务数据处理量
这个字段可枚举,数值不宜太多,多了表数据