Hive 的执行原理是高频面试题。因为后面的调优或者数据倾斜的处理都是在对其执行原理清楚的基础上进行的。今天以 GROUP BY
为例讲解一下。
1.直观的角度
下表名叫 table1:
id | name | number |
---|---|---|
1 | aaa | 2 |
2 | aaa | 3 |
3 | bbb | 4 |
4 | bbb | 5 |
5 | ccc | 6 |
6 | ddd | 7 |
7 | eee | 7 |
8 | bbb | 5 |
9 | ccc | 6 |
如果执行下面的语句:
SELECT name
FROM table1
GROUP BY name;
容易得到结果:
name |
---|
aaa |
bbb |
ccc |
ddd |
eee |
那么到底怎么执行的呢?我们先拆解一下这个过程。SQL 的执行顺序如下:
- FROM JOIN
- WHERE
- GROUP BY
- HAVING
- SELECT(窗口函数在这里执行)
- ORDER BY
- LIMIT
于是可以得到:
第一步,先执行 FROM table1
,得到的还是原来的表:
id | name | number |
---|---|---|
1 | aaa | 2 |
2 | aaa | 3 |
3 | bbb | 4 |
4 | bbb | 5 |
5 | ccc | 6 |
6 | ddd | 7 |
7 | eee | 7 |
8 | bbb | 5 |
9 | ccc | 6 |
第二步,再执行 FROM table1 GROUP BY name
得到下面虚拟的表:
id | name | number |
---|---|---|
1 2 | aaa | 2 3 |
3 4 8 | bbb | 4 5 5 |
5 9 | ccc | 6 6 |
6 | ddd | 7 |
7 | eee | 7 |
第三步,执行 SELECT name FROM table1 GROUP BY name
,即可得到答案。
name |
---|
aaa |
bbb |
ccc |
ddd |
eee |
注意:可以看到 id 和 number 里面有多行,这也就是使用了 GROUP BY 后选择其他字段必须使用聚合函数 (如 SUM、MAX 等)的原因。例如,执行下面的 SQL :
SELECT name,SUM(number) AS total
FROM test
ORDER BY name;
可以得到如下结果:
name | total |
---|---|
aaa | 5 |
bbb | 14 |
ccc | 12 |
ddd | 7 |
eee | 7 |
如果是 GROUP BY
多字段,道理也一样,例如:
SELECT name,number
FROM table1
GROUP BY name,number;
此时就把 name 和 number 看成整体分组就好了:
id | name | number |
---|---|---|
1 | aaa | 2 |
2 | aaa | 3 |
3 | bbb | 4 |
4 8 | bbb | 5 |
5 9 | ccc | 6 |
6 | ddd | 7 |
7 | eee | 7 |
最后再得到结果:
name | number |
---|---|
aaa | 2 |
aaa | 3 |
bbb | 4 |
bbb | 5 |
ccc | 6 |
ddd | 7 |
eee | 7 |
2.从 MapReduce 的角度看
- map 阶段,将 group by 后的字段组合作为 key,如果 group by 单字段那么 key 就一个。
- 将 group by 之后要进行的聚合操作字段作为值,如要进行 count,则 value 是 1;如要 sum 另一个字段,则 value 就是该字段。
- shuffle 阶段,按照 key 的不同分发到不同的 reducer。注意此时可能因为 key 分布不均匀而出现数据倾斜的问题。
- reduce 阶段,将相同 key 的值累加或作其他需要的聚合操作,得到结果。
例如执行下面的语句:
select rank,isonline,count(*)
from city
group by rank, isonline;
3.GROUP BY 的特性
- 使用了 reduce 操作,受限于 reduce 数量,通过参数
mapred.reduce.tasks
设置 reduce 个数。 - 输出文件个数与 reduce 数量相同,文件大小与 reduce 处理的数量有关。
4.GROUP BY 的问题
- 网络负载过重。
- 出现数据倾斜(我们可以通过
hive.groupby.skewindata
参数来优化数据倾斜的问题)。
5.GROUP BY 出现数据倾斜的调优方法
set hive.map.aggr=true
,即开启 map 端的 combiner,减少传到 reducer 的数据量,同时需设置参数hive.groupby.mapaggr.checkinterval
规定在 map 端进行聚合操作的条目数目。- 设置
mapred.reduce.tasks
为较大数量,降低每个 reducer 处理的数据量。 set hive.groupby.skewindata=true
,该参数可自动进行负载均衡。生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。
6.GROUP BY 和 DISTINCT 去重的区别
- GROUP BY 是按照 key 进行 hash 分散到多个 Reduce 中处理,速度快;
- DISTINCT 是把要处理字段全部放到一个 Reduce中 处理,速度慢;
- 结论:尽量用 GROUP BY 替代 DISTINCT。不过如果 key 类别太多会造成分桶过多。例如 GROUP BY user_id 和 DISTINCT user_id,user_id 可能很多,分桶太多。
欢迎关注。