目录
1、数据准备
2、实例解读
扩展知识:group by带来的数据倾斜处理、与distinct的区别
环境
hive (mydb)> select * from page_view;
OK
page_view.pageid page_view.userid page_view.time
1 111 9:08:01
2 111 09:08:13
1 222 09:08:14
1 333 10:56:00
1 444 12:16:45
hive (mydb)> select * from page_user;
OK
page_user.userid page_user.age page_user.gender
hive (mydb)> select * from page_user;
OK
page_user.userid page_user.age page_user.gender
111 25 female
222 32 male
333 25 male
444 25 female
page_view表
:是一个网站 或APP页面访问(或称浏览)表(也可以理解为 页面访问日志)。比如:第一行表示用户(userid)111访问了页面(pageid)1 ,访问时间是9:08:01
。page_user表
:是访问页面的用户(user)信息,字段有userid、age、gender。需求:访问每个页面的用户信息(暂时 只需得到用户年龄)
1)创建一个新表用于存储页面id、用户年龄:pv_users
create table pv_users (pageid string,age string);
2)得到新表的数据:
insert overwrite table pv_users
select pv.pageid,pu.age
from page_view pv
join page_user pu
on (pv.userid=pu.userid);
3)查看新表的数据:
hive (mydb)> select * from pv_users;
OK
pv_users.pageid pv_users.age
1 25
2 25
1 32
1 25
1 25
很显然:
新增一个需求:每个页面上,统计访问的用户年龄。
select pageid,age,count(1) as cnt
from pv_users
group by pageid,age;
OK
pageid age cnt
1 25 3
1 32 1
2 25 1
group by:按照某些字段的值进行分组,有相同值的行 放到一起。
解读方式 1:SQL语句执行的逻辑过程
select *
from pv_users
group by pageid,age;
(tok_table_or_col pageid) (tok_table_or_col age)
1 25
1 32
2 25
-------------------------------
select age
from pv_users
group by age;
age
25
32
select age
from pv_users
group by age;
from pv_users
语句,结果是和pv_users
一样的,即原来的表内容;from pv_users group by age
,以字段age
为基准进行分组(group by
),age
值相同的行【合并】在一起,即分组(group)到一起,为了便于理解,可认为:若还有其他字段(如pageid
)写到一个单元格中;
count(age 或* 或1)
、sum(字段)
;group by 字段1, 字段2,...
,那么就将字段1, 字段2,...
看作一个整体。select age
,对上一步结果执行select
语句,即 得到age
列的数据。下方SQL语句可以一样的方式去理解,只不过要需要结合实际生产环境的场景来:
select pageid,age
from pv_users
group by pageid,age;
pageid age
1 25
1 32
2 25
-------------------------------
select pageid
from pv_users
group by pageid,age;
pageid
1
1
2
-------------------------------
select pageid,age,count(*)
from pv_users
group by pageid,age;
pageid age _c2
1 25 3
1 32 1
2 25 1
-------------------------------
select age,count(*)
from pv_users
group by pageid,age;
age _c1
25 3
32 1
25 1
-------------------------------
select age
from pv_users
group by pageid;
FAILED: SemanticException [Error 10025]: Line 1:7 Expression not in GROUP BY key 'age'
注意:select
后面的非聚合列(即 普通列)必须是出现在group by中
的,否则报错如上。【聚合列】,即进行聚合操作的列,比如:count(1),sel_expr(聚合操作)
。
解读方式 2:通过MapReudce的执行原理来理解 group by
:
select age
from pv_users ---->Map端执行
group by age; ---->Reduce端执行
group by
的执行会经过reduce操作,必然受限于reduce数量(set mapred.reduce.tasks=n;
,不使用默认的话,可自行设置),输出文件个数跟reduce数量相同、文件大小跟reduce处理的数量有关。
explian
命令,用于查看查询计划:
hive (mydb)> explain select pageid,age,count(1) as cnt
> from pv_users
> group by pageid,age;
OK
Explain
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 depends on stages: Stage-1
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: pv_users
Statistics: Num rows: 5 Data size: 20 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: pageid (type: string), age (type: string)
outputColumnNames: pageid, age
Statistics: Num rows: 5 Data size: 20 Basic stats: COMPLETE Column stats: NONE
Group By Operator
aggregations: count(1)
keys: pageid (type: string), age (type: string)
mode: hash
outputColumnNames: _col0, _col1, _col2
Statistics: Num rows: 5 Data size: 20 Basic stats: COMPLETE Column stats: NONE
Reduce Output Operator
key expressions: _col0 (type: string), _col1 (type: string)
sort order: ++
Map-reduce partition columns: _col0 (type: string), _col1 (type: string)
Statistics: Num rows: 5 Data size: 20 Basic stats: COMPLETE Column stats: NONE
value expressions: _col2 (type: bigint)
Reduce Operator Tree:
Group By Operator
aggregations: count(VALUE._col0)
keys: KEY._col0 (type: string), KEY._col1 (type: string)
mode: mergepartial
outputColumnNames: _col0, _col1, _col2
Statistics: Num rows: 2 Data size: 8 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
Statistics: Num rows: 2 Data size: 8 Basic stats: COMPLETE Column stats: NONE
table:
input format: org.apache.hadoop.mapred.TextInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
Stage: Stage-0
Fetch Operator
limit: -1
Processor Tree:
ListSink
Time taken: 0.078 seconds, Fetched: 48 row(s)
hive (mydb)>
【Hive-Group By(GroupByOperator类) 源码-官方】
【大佬博文-Hive 工作流程源码分析】
扩展知识:
1、问题:由于group by
带来的数据倾斜(Skew In Data),导致性能变差
set hive.groupby.skewindata=true;
参数来优化数据倾斜的问题,默认为false
。但每个参数都是双刃剑),参数设置为true
时,会生成的查询计划会有两个MR Job:即设置了这个参数后的MR执行流程
2、group by
与distinct
区别以及性能比较
distinct,译作:有区别的、不同的
Hive并不惧怕数据量有多大,而是数据倾斜。
数据:pv_users表
pv_users.pageid pv_users.age
1 25
2 25
1 32
1 25
1 25
-------------------------------------
select count(distinct age) as age_cnt
from pv_users;
age_cnt
2
-------------------------------------
select count(t.age) as age_cnt from
(
select age from pv_users group by age
) t;
age_cnt
2
当处理大量数据时(比如 近8亿条记录,总大小108GB。当然这测试时只有5条数据),从MR执行打印中可观察到:
distinct(key)
,会将所有的key都shuffle到一个reducer中,就会发生数据倾斜。distinct(key)
会将key 的所有值都加载到内存中,数据量太大时,容易OOM。即 count(distinct key)
内存消耗大,但查询快。group by
是将key排序,它的空间复杂度小,在时间复杂度允许的情况下,可以发挥他的空间复杂度优势。因此,数据量太大时,不推荐用distinct
,尽管可读性更好。
一条SQL语句中,同时有group by
、distinct
语句,执行顺序是:先group by
,后distinct
。