hive优化
hive本质上将sql语句解析成mapreduce程序运行,对于mapreduce的优化同样也会对hive sql的运行起到一定的优化作用,
优化的大致思路大致目标是:增加任务线程来处理、避免数据倾斜
1.增加任务线程来处理
- 增加reduce数量
- reduce数量计算公式:N=min(最大的reduce数,总输入数据量/每个reduce的处理数据大小)
- 每个Reduce处理的数据量默认是256MB,hive.exec.reducers.bytes.per.reducer=256123456
- 每个任务最大的reduce数,默认为1009 hive.exec.reducers.max=1009
- 通过group by来增加reduce数量,默认将同一阶段的key发往同一个reduce
-
例如 select count(distinct s_id) from score是会将数据的全发往一个reduce中,改成select count(1) from (select s_id from score group by s_id),select col1 [,col2] ,count(1)from table where condition //Map端执行 group by col1 [,col2] //Reduce端执行 [having] //Reduce端执行
- 每个job的Reduce个数可以通过参数mapred.reduce.tasks 指定,输出文件个数与reduce数相同,文件大小与数据量有关
- order by排序是全局排序,会将数据发到同一个reduce中去,应尽量少用
- MapReduce内部排序(Sort By) ,对于全局不是排序,单个reduce内部有序
- 分区排序(DISTRIBUTE BY)结合sort by 一起使用(DISTRIBUTE BY语句要写在SORT BY语句之前。)
- cluster by ;当distribute by和sort by字段相同时 cluster by=distribute by+sort by
-
- reduce数量计算公式:N=min(最大的reduce数,总输入数据量/每个reduce的处理数据大小)
- map数量调整
- map的数量的计算公式map_num=min(split_num,max(default_num,goal_num)) ;默认 = total_size/block_size; 因为split_size = bloct_size
- set mapred.map.tasks =10; 只是一个期望值,想增加就这一个比较大的值,
- split_size=max(mapred.min.split.size,block_size); 实际切片大小
- mapred.min.split.size是每一个map文件的最小,如果想减少map数量,设置一个比较大的值
- split_num=total_size/split_size; 切片数量
- map端聚合
- set hive.map.aggr = true(默认能在map端聚合的操作会在map端聚合)
- 例如;原表a只有一个文件,可以先拆分成三个文件,这样每一个map任务只处理三分之一的数据量,效率会提升很多
-
set mapreduce.job.reduces =3;
create table a_1 as
select * from a
distribute by rand(3);
-
- map的数量的计算公式map_num=min(split_num,max(default_num,goal_num)) ;默认 = total_size/block_size; 因为split_size = bloct_size
- map和reduce的数量不是越多越好,因为启动很好比较慢,对于小文件比较多,应尽量合并小文件
- Map最大输入大小:set mapred.max.split.size=256000000;
- 开启map任务执行前合并小文件:Map前进行小文件合并
2.避免数据倾斜
- sum,count,max,min等UDAF,不怕数据倾斜问题,hadoop在map端的汇总合并优化,使数据倾斜不成问题。
- count(distinct )在大数据条件下,效率比较低,
- 开启优化参数
hive.groupby.skewindata=
true 可以开启一优化任务,第一个job会将map输出随机分配到reduce中,实现负载均衡,每一个reduce做部分聚合,第二个job对预处理的结果按照Group By Key分布到Reduce中,完成最总的聚合
3.表的优化
- 避免笛卡尔积
- 分区裁剪: 分区表查询最好带上分区
- 列裁剪:尽量避免使用select *
- 动态分区
- set hive.exec.dynamic.partition = true; 开启动态分区
set hive.exec.dynamic.partition.mode = nonstrict; 设置为非严格模式,默认strict
set hive.exec.max.dynamic.partitions = 1000; 所有执行MR的节点上,最大一共可以创建多少个动态分区
set hive.exec.max.dynamic.partitions.pernode = 100;每个执行MR的节点上,最大可以创建多少个动态分区
set hive.exec.max.created.files = 100000; 整个Job中,最大可以创建多少个HDFS文件
set hive.error.on.empty.partition = false; 有空分区生成时,是否抛出异常 - 注意在SELECT最后几个字段,必须对应前面PARTITION (month,day)中指定的分区字段,包括顺序。
- 例如 INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time)
SELECT id, time, uid, keyword, url_rank, click_num, click_url, p_time
FROM ori_partitioned;
- set hive.exec.dynamic.partition = true; 开启动态分区
其他参数优化
- 使用EXPLAIN(执行计划)
- explain select * from course
- explain extended select * from course;
- JVM重用 : Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务,每个Task运行在一个独立的JVM进程中,JVM重用可以使得JVM实例在同一个job中重新使用N次,减少资源的开启次数
- 指定重用次数:mapreduce.job.jvm.numtasks
- 缺点:开启JVM重用将一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放,如果哪一个task执行时间比其他长,那么保留的插槽就会一直空闲着却无法被其他的job使用
- 并行执行:有些sql之间是不相关的,可以并行的一起执行
- 打开任务并行执行:set hive.exec.parallel=true;
- 同一个sql允许最大并行度:set hive.exec.parallel.thread.number=16; //默认为8。
- 尽量避免使用mr
- 将hive-default.xml.template文件中hive.fetch.task.conversion设置为more(默认也是more),这样在全局查找和指端查找,limit查找不会走mapreduce,例如select * from score; select s_score from score; select s_score from score limit 3;
- 关闭推测执行:如果没有关闭,那么当一个任务执行时间很长时,程序会认为程序卡了,再开一个任务来执行
- mapred.reduce.tasks.speculative.execution
- hive.mapred.reduce.tasks.speculative.execution