在 Hive 选择 MapReduce 为执行引擎的前提下,由于使用了 distinct,导致在 map 端的 combine 无法合并重复数据;对于这种 count() 全聚合操作时,即使设定了 reduce task 个数,hive 也只会启动一个 reducer。这就造成了所有 map 端传来的数据都在一个 task 中执行,成为了性能瓶颈。

0x00 单字段 count(distinct) 优化

典型场景:某一天的日志数据有上亿条,计算当天的活跃用户数;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
---常规实现
select count(distinct uuid) from log where dt = '2019-11-15';

---1.优化方式一
select count(*) from (
select distinct uuid from log where dt = '2019-11-15'
) t;

---2.优化方式二
select count(*) from (
select uuid
from log where dt = '2019-11-15'
group by uuid
) t;

---3.优化方式三
select sum(part)
from (
select tag, count(*) as part
from (
select uuid, cast(rand() * 100 as bigint) as tag
from log where dt = '2019-11-15'
group by uuid
) t1
group by tag
) t2;

---辅助优化参数
set mapreduce.job.reduces=100;

0x01 多字段 count(distinct) 优化

典型场景:某一天的日志数据有上亿条,计算当天的活跃设备数和活跃用户数;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---常规实现
select count(distinct device), count(distinct member)
from log where dt = '2019-11-15';

---通用优化方式
select count(device), count(member)
from (
select device, null as member
from log where dt = '2019-11-15'
group by device
union all
select null as device, member
from log where dt = '2019-11-15'
group by member
) t;

---辅助优化参数
set mapreduce.job.reduces=100;

0x02 优化思路

由于 Hive 的执行引擎是 MapReduce,而 MapReduce 的设计思想是分而治之,所以解决数据倾斜的根本是将 map 输出数据经过 hash 均匀地分配到 reduce 中,再进行聚合等。

参考文献

MapReduce执行过程详解
HiveQL的Select语句
Hive – Distinct 的实现