最近碰到有人问我,一个hql当中,如果有一个join,然后 有一个group by 操作。这个时候的map有多少个;
其实之前也看执行计划。今天有空就研究了一下,一看这里面的学问还真的不少。下面就以一个例子来说明:
explain select s0.sno,count(distinct s0.sname) from student s0 left outer join student1 s1 on (s0.sno=s1.sno) group by s0.sno;
这里有两张表,一个student表,一个student1表。两个表的sno,做一个join操作。并且对 sno进行分组,然后将分组完成之后的表达式中的sname不同的条数统计出来。
下面我们执行这个解释语句,执行的结果如下:
STAGE DEPENDENCIES:
Stage-5 is a root stage
Stage-2 depends on stages: Stage-5
Stage-0 depends on stages: Stage-2
--这里将整个的语句划分为三个阶段,Stage-5作为根目录,然后Stage-2是对Stage-5的依赖。最后Stage-0将结果显示出来
STAGE PLANS:
Stage: Stage-5
Map Reduce Local Work
Alias -> Map Local Tables:
s1 --这里先进行第一次的map,然后这里将第二张表的表名加载进来,标记别名。这里进行的Fetch 操作。
Fetch Operator
limit: -1
Alias -> Map Local Operator Tree: --这里是本地的map 操作加载数据。
s1
TableScan
alias: s1
Statistics: Num rows: 40 Data size: 160 Basic stats: COMPLETE Column stats: NONE
HashTable Sink Operator --这里进行的hash操作。下面是条件表达式。
condition expressions:
0 {sno} {sname}
1
keys:
0 sno (type: int)
1 sno (type: int)
Stage: Stage-2 --然后进入到第二阶段。在这个阶段才是真正的map reduce阶段。
Map Reduce
Map Operator Tree: --这里进行的map操作。
TableScan --首先扫描坐标的表。
alias: s0
Statistics: Num rows: 3 Data size: 320 Basic stats: COMPLETE Column stats: NONE
Map Join Operator --这里采用的hive吧这条sql解释为了map join。
condition map:
Left Outer Join0 to 1 --这里进行连接。将左表和右表进行连接。
condition expressions:
0 {sno} {sname}
1
keys:
0 sno (type: int)
1 sno (type: int)
outputColumnNames: _col0, _col1 --这里输出两列。分别是sno,sname。
Statistics: Num rows: 44 Data size: 176 Basic stats: COMPLETE Column stats: NONE
Select Operator --在这里执行了一次select操作。
expressions: _col0 (type: int), _col1 (type: string)
outputColumnNames: _col0, _col1
Statistics: Num rows: 44 Data size: 176 Basic stats: COMPLETE Column stats: NONE
Group By Operator --在hive当中,为了优化sql,在数据进入到reduce端之前,会对数据进行简单的分组。在这里将分组的sno和sname,作为健,输出的三个列的数据。
aggregations: count(DISTINCT _col1)
keys: _col0 (type: int), _col1 (type: string)
mode: hash
outputColumnNames: _col0, _col1, _col2 --这里进行三个列的输出对于这 _col2,我的理解是两个列组成的和。
Statistics: Num rows: 44 Data size: 176 Basic stats: COMPLETE Column stats: NONE
Reduce Output Operator --在这里我们可以看到在进入到reduce之前,需要进行一些操作。
key expressions: _col0 (type: int), _col1 (type: string)
sort order: ++ --这里对数据进行分组排序,也是shuffle过程的前期准备。对同一个map task端的数据进行排序。
Map-reduce partition columns: _col0 (type: int) --这里将对map出的数据进行分组,这里是将数据按照group by的列名称进行分作输送到不同的partition当中。
Statistics: Num rows: 44 Data size: 176 Basic stats: COMPLETE Column stats: NONE
Local Work:
Map Reduce Local Work
Reduce Operator Tree: --在这里我们的数据进入到reduce阶段的处理。
Group By Operator --在这里才是正真的分组。
aggregations: count(DISTINCT KEY._col1:0._col0) --然后我们将各个组当中的数据按照我们分好的组,然后统计这个组当中不同的sname的数量。
keys: KEY._col0 (type: int)
mode: mergepartial
outputColumnNames: _col0, _col1 --这里将统计的好书进行输出。
Statistics: Num rows: 22 Data size: 88 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: _col0 (type: int), _col1 (type: bigint)
outputColumnNames: _col0, _col1
Statistics: Num rows: 22 Data size: 88 Basic stats: COMPLETE Column stats: NONE
File Output Operator --这里是文件的数据操作,也是落地到磁盘的操作。
compressed: false
Statistics: Num rows: 22 Data size: 88 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
其实这里我们看到数据写磁盘的操作。在map完了之后的阶段。可能是因为数据量不够大的缘故吧。
另外这里采用的是map jion的操作也没有看到在join的时候,出现shuffle的过程。我觉得可能也是数据量太小了吧。直接加载进了分布式缓存当中。才造成现在的现象。
如果有解释不对的地方,如果有人看到麻烦指点。