本篇文章来源于《Hive性能调优实现》。
在HiveSQL里面经常用到的过滤方法就是使用where子句,例如:
explain
select * from student_tb_seq where s_age=19 and s_name like '%红%'
and s_score in (100,50,22);
where子句在执行计划中以filter操作表示,代码如下:
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: student_tb_seq
Filter Operator
predicate: (((s_age = 19) and (s_name like '%红%')) and (s_score) IN (100, 50, 22)) (type: boolean)
Select Operator
expressions: s_no (type: string), s_name (type: string),s_birth (type:
string), 19 (type: bigint), s_sex (type: string), s_score(type:
bigint), s_desc (type: string)
...
从以上信息可以看到,where的filter操作发生在Map操作阶段,表示在 Map阶段就已经执行完数据过滤。
上述SQL案例可以转换为MapReduce代码表示:
map(inkey,invalue,context):
//在Map区读取一般的Hive表时,在MapReduce引擎看来只是一行字符串
//如果要获取字符串的列,需要对字符串按表的分隔符进行分割
colsArray= invalue.split("\t")
s_age=colsArray[3]
s_name=colsArray[2]
s_score=colsArray[5]
//如果不是19,则丢弃整行数据
if s_age !=19:
return;
//如果在名字中找不到"红"的字,会返回索引-1,抛弃整条数据
if s_name.indexof("红")==-1:
rerturn;
//如果分数不是100,50
if s_score !=100 or s_score!= 50 or s_score!=22:
return
//符合上面3个规则的,将数据输出
context.write(null,s_age+'\t'+s_name+'\t'+s_score)
//只有where子句用不到reduce()方法
reduce(inkey, invalue,context):
从上面的代码中我们可以看到,where子句发生在Map端,Map端的任务在执行时会尽可能将计算逻辑发送到数据所在的机器中执行,这时候可以利用分布式计算的优点,多机同时执行过滤操作,快速过滤掉大量的数据。如果能够在Map端过滤掉大量的数据,就可以减少跨机器进行网络传输到 Reducer端的数据量,从而提升Map后面环节的处理效率。
通过where子句的过滤模式,启示我们对于一个作业应尽量将其放在前面环节进行数据过滤,对于一个由几个大的作业组成的任务,为了提升整体的效率,也应尽可能地让前面的环节过滤掉大量非必须的数据。
例如,对于一个HiveSQL脚本,通常由多个作业组成,转换成MapReduce任务,表示为Map1-Reduce1-Map2-Reduce2...-MapN-ReduceN。如果能够在前面的Map或者Reduce中过滤掉大量的数据,就有利于减少后面的作业处理和传输的数据量,从而提高整体的作业性能。
例如:尽早过滤掉不需要的数据
select count(s_age) from (
select s_age, count(1) num
from student_tb_seq
group by s_age
) a
where s_age <30 and num>20;
--处理后的例子代码如下:
select count(s_age) from (
select s_age, count(1) num
from student_tb_seq
where s_age<30
group by s_age
having count(1)>20
) a;
PS:有问题欢迎大家指正与讨论