数据倾斜常见特征
- 同一个stage的task中,有个别的task执行时间明显比其他的要长得多,整体stage临近结束但一直卡着很长一段时间。
- 整体任务数据量不大,经常OOM(内存溢出)。即使通过参数增大了内存,已经比一般的任务要大得多了,而且减少了每个task处理的数据量,依然无济于事。
起因
shuffle阶段key的分布不均,单个task读取数据过多,导致执行时间变长,甚至撑爆内存。
HiveSQL或SparkSQL会引起shuffle的操作:reduce join、group by
- reduce join操作的key:关联的字段(on子句上的字段)
- group by操作的key:聚合列(group by子句上的字段)
注:Hive on Spark和Spark SQL是两种不同的东西,请注意区分。
解决原则
- 二选一:
1.1 消除数据倾斜:只有一种可能:reduce join改map join
1.2 缓解数据倾斜:让每个key处理的平均数据量更均匀 - 不过度优化:没有出现性能问题就不优化。优化后务必与原来的方案进行比较。
- 当前新版本的SQL引擎(Hive3.0、Spark2.X),大部分已经内置优化了group by,大多数情况下不需要做优化。
解决数据倾斜之后可能出现的现象
- 可能有更多的数据操作,产生更多的IO(map join、broadcast join这类操作基本忽略不计)
- 或许会增加处理时长,但单个task处理数据量上限可下调,任务总体资源消耗得到优化,运行状态也会更稳定。
解决思路
join
1. 存在一张小表
若有一张表比较小,通过参数把SQL转为map join(HiveSQL)或者broadcast join(SparkSQL)。
这两个优化操作在对应的引擎里都是默认开启的。
- SparkSQL broadcast join默认判断标准是表小于10MB。(参数:
spark.sql.autoBroadcastJoinThreshold
,设置为-1时关闭broadcast join)
开关为: - HiveSQL map join的默认判断标准是表小于25M。(参数:
hive.mapjoin.smalltable.filesize
,开关:hive.auto.convert.join
)
参考资料:
2. 不存在小表,表都比较大
表都比较大,不适合map join和broadcast join。部分云服务商的SQL工具会有参数可以进行优化,自建集群大部分情况下要借助随机数改写SQL以缓解数据倾斜。
根据异常值是否需要保留展示、异常值的数量多少,处理方式略有不同。
group by
当前一些版本SQL引擎(Hive3.0以上、Spark2.X),group by的优化做得已经很好了,基本不怎么需要额外优化,以下可当作是优化原理解析。
没有count(distinct)
- 随机数双重聚合
给原group by的列加上cast(rand() * N as int)进行第一次聚合,然后对子查询再做一次聚合 - 加参数:
set hive.groupby.skewindata=true;
两种方法原理相近,都是通过随机数把key尽可能均匀分摊到不同的分区中,通过多次的操作完成聚合。
- 示例链接:groupby没有count(distinct)的场景
有count(distinct)
- 在hive3.0及以上版本,设置参数:
set hive.optimize.countdistinct=true;
。此为默认设置。
注:set hive.groupby.skewindata=true;
时不支持multi distinct,且在hive1.2.0以前的版本在混有single distinct的场景下会导致数据统计异常,这种场景下请勿设置。 - Spark2.X版本(疑似从2.0开始)能够自动优化。
- 在其他的旧版本,有可能没有默认参数解决数据倾斜。这个时候就需要借助hive和spark的优化思路做出手工调整。