HIVE SQL优化的两个思路

上一篇我们介绍了关系型数据库SQL的优化主要是索引和减少数据量,本文以大家常用的HIVE SQL为基础来介绍如何优化SQL的运行速度。

下面是本次分享的逻辑和顺序:

 

HIVE SQL优化的两个思路_第1张图片

HIVE SQL优化的核心

 

数据倾斜

大家知道大数据的核心之一就是数据量大,所以数据量很大对于大数据本身不是挑战,否则就不叫大数据了。大数据最怕的就是数据倾斜,所谓的倾斜就是所有的task都放到一个节点(暂且理解为一台机器)去这性,这样大数据的大(分布式集群)的优势就无法发挥出来。就想下面这整图,看着是一堆处理器,实际干活的却只有一个。

HIVE SQL优化的两个思路_第2张图片

为什么会产生数据倾斜呢?接触过大数据的都知道,大数据任务的执行通常来说都是分阶段执行,上一个阶段执行完产生中间结果,然后下一个阶段拉取上个阶段的执行结果,而大数据又是分布式、多任务并行的,那么每个任务如何拉取自己需要的那份数据呢?这通常使用的是hash算法,在这种算法下,如果数据中数据key(hash分组依赖的健)分布不均匀的话就会造成大量的数据分到一个节点上,而极少的数据分给另外的节点。分配的不公平自然也就造成了计算时间的不一致,数据少的几点很快执行完但是必须要等到分配数据多的节点执行完才能输出最终结果。这就是数据倾斜的通俗解释。

减少数据量

虽然我们说了大数据并不畏惧超大量数据,但是站在优化的角度来说如果能减少不必要的参与计算的数据还是具有重要的意义的。减少数据量能从两个方面来优化,首先是减少磁盘IO,尤其是对于MR这种计算框架中间临时计算结果是要写到磁盘的,磁盘的速度要比内存慢很多。其次减少数据量还可以减少网络IO,我们知道大数据都是集群式部署的,不同节点之间要进行数据的交换必要要通过网卡,网卡相比磁盘有慢了不少。所以,单从以上两个方面来说减少数据量同样是优化中不可或缺的。

 

从两个维度来谈如何优化

 

HIVE SQL书写层面上的优化

1、使用group by 代替count(distinct)。如:

优化前:select count(distinct name) from student;

优化后:select count(*) from (select name,count(*) from student group by name );

说明:distinct 在reduce阶段会把所有的任务都分配一个reduce task执行,如果数据量过大会很慢,所以建议先分组然后再count(*)

2、使用分区裁剪和列裁剪:如果是分区表只取我们需要的分区;不要select * ,而是选择我们需要的数据列。如:

优化前:select  * from student ;

优化后:select  name,age,sex from student where dt=$dt;

说明hive的分区表是在hdfs上是按目录存放的,我们指定分区的话就会指hive只访问特定的目录减少需要读取的数据量。select 的时候指定特定的列也只会扫描特定的列不会扫描全表,从而提高执行速度。

3、使用sort by 和distribute by 代替order by ,如:

优化前:select name,score,age from student order by score desc;

优化后:select name,score,age from student distribute by score sort by age desc;

说明:使用order by 和distinct 类似,在reduce阶段都会把所有的任务集中到有个reduce task中去就算,如果数据量过大就容易出现跑数时间过长的问题。使用sort by和distribute by后MR会根据情况启动多个reduce来排序,如果不加distribute by 的话map后的数据会随机分配到每个reducer中,如果加上的话会根据指定的分区键来对map后的数据分区,保证每个reducer中的数据是有序的。

4、使用谓词下推,谓词下推就是把where条件前置,提前过滤掉不需要的数据。如:

优化前:select t1.name,t2.city_name from student  t1 left join t2 on t1.city_id=t2.id 

where t1.age>=18 and t2.city_name<>'北京';

优化后:select t1.name,t2.city_name from 

(select  city_id, name from studet where age>=18) t1

left join

(select city_name,id from city where city_name<>'北京');

说明:参考我们之前介绍的SQL执行顺序,使用谓词下推可以提前把不需要数据剔除减少join的数据,进而减少执行消耗的时间。

5、针对join的优化,使用map join,多表join时小表写在最前面,关联条件满足时尽量各表使用同一键进行关联。如:

优化前:select t1.order_id,t1.price,t2.city_name from order t1

left join

dim_city city 

on t1.city_id=t2.id

优化后:select t2.order_id,t2.price,t1.city_name from dim_city t1

left join

order t2

on t1.id=t2.city_id

说明:MR在执行的过程中会默认把第一个表加载到内存中然后逐条跟后面的表匹配,如果第一个表数据量过大加载到内存中就会有OOM的风险,所以在多表关联且明确知道哪个表数据量更小的情况下建议把数据量的表写在最左边。

6、其他一些需要注意的地方:

1、join的保证关联健的数据类型一致,不一致的话使用cast进行转换;

2、join的时候如果关联健空值较多,优先把空值过滤掉;如果key值集中度过高先使用随机数打散再处理。

3、避免使用笛卡尔积和not in 操作。

 

HIVE 参数配置优化

1、group by 时的参数配置,group时容易数据数据倾斜(分组的ID分布不均匀),另外在map时开启预聚合可以减少reduce端需要处理的数据量,进而提高执行速度。具体配置如下:

hive.map.aggr;--开启map端预聚合

hive.groupby.skewindata;--数据倾斜时起作用,原理是启动两个mr作业,前一个作业负责把随机打散随机分配到不同的reducer上。

2、join时参数配置的优化,主要是开启map join 及分桶join。具体如下:

hive.auto.convert.join;--自动开启map join

hive.optimize.bucketmapjoin;--分桶join优化

hive.optimize.skewjoin;--join 时有group by 时开启

3、调整map数,调整的一般性原则是:如果输入文件是少量大文件,就减少mapper数;如果输入文件是大量非小文件,就增大mapper数;如果是大量的文件就先合并小文件。具体配置如下:

mapred.min.split.size;--最小分片大小

mapred.max.split.size;--最大分片大小

mapred.map.tasks;--设置map task任务数

这需要强调的是map任务的数是根据这个规则计算的,map_num = MIN(split_num, MAX(default_num, mapred.map.tasks)),也就是说是根据分片数和设置的map task的数目的最小值,所以如果想减少mapper数,就适当调高mapred.min.split.size,split数就减少了。如果想增大mapper数,除了降低mapred.min.split.size之外,也可以调高mapred.map.tasks。

4、调整reducer数,这是一个有利有害的参数,一般来说增加reducer数可以提高执行速度,但是会增加大量的小文件,小文件过多的话会影响map的执行速度。具体参数如下:

mapred.reduce.tasks;--设置reducer的数量

hive.exec.reducers.bytes.per.reducer;--设置每个reducer能处理的最大数据量 默认是1G

hive.exec.reducers.max;--设置每个job最大的reducer的数量

5、合并小文件,具体配置如下:

org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;--输入阶段合并小文件

hive.merge.mapfiles=true;hive.merge.mapredfiles=true;--输出阶段小文件合并

6、启用压缩:压缩可以减少数据量,进而减少磁盘和网络IO。具体配置如下:

hive.exec.compress.intermediate;--开启压缩

hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;--使用Snappy压缩

hive.intermediate.compression.type;--配置压缩对象 快或者记录

7、设置并行模式和本地模式,并行模型主要针对uoion 操作,本地模式主要针对数据量小,操作不复杂的SQL。具体配置如下:

hive.exec.parallel=true;--开启并行模式

hive.exec.parallel.thread.number;--设置并行执行的线程数

hive.exec.mode.local.auto;--开启本地执行模模式

8、设置合适的数据存储格式,hive默认的存储格式是TextFile,但是这种文件格式不使用压缩,会占用比较大空间,目前支持的存储格式有SequenceFile、RCFile、Avro、ORC、Parquet,这些存储格式基本都会采用压缩方式,而且是列式存储,使用这些格式的话文件不能直接导入。设置存储格式一般在建表时指定,如:

ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.orc.OrcSerde'

STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'

指定只用orc存储格式

8、使用严格模式,严格模式主要是防范用户的不规范操作造成集群压力过大,甚至是不可用的情况,只对三种情况起左右,分别是查询分区表是不指定分区;两表join时产生笛卡尔积;使用了order by 排序但是没有limit关键字。具体配置如下:

hive.mapred.mode=strict;--开启严格模式

 

如果你只是单纯的使用hive来查询数据建议重点关注hive sql本身书写优化,只要SQL写得好,一样比别人跑得快。如果你是数据开发可以接触到hive配置的话可以参考hive的参数配置部分进行作业的调优。

 

备注:以上只是个人经验的一些总结,参数调节部分来说相对复杂一些,涉及的参数较多,需要不同参数联合设置不断实践才能达到最优的效果。另外随着hive版本的升级,其本身也在不断的优化,如map join在新的版本中已经默认开启,建议一些细节的配置参考官网来配置。

你可能感兴趣的:(SQL优化,大数据,大数据,hive,hadoop)