Hive中对某些情况的查询可以不必使用MapReduce计算,例如:select * from score 。这种情况下,Hive可以直接度去score对应的存储目录下的文件,然后输出查询结果到控制台,通过设置hive.fetch.task.conversion参数,可以控制查询语句是否走MapReduce。
案例演示:
-把hive.fetch.task.conversion设置成none,然后执行查询语句,都会执行mapreduce程序。
set hive.fetch.task.conversion=none;
select * from score;
select s_score from score;
select s_score from score limit 3;
把hive.fetch.task.conversion设置成more,然后执行查询语句,如下查询方式都不会执行mapreduce程序。
set hive.fetch.task.conversion=more;
select * from score;
select s_score from score;
select s_score from score limit 3;
大多数的Hadoop Job是需要Hadoop提供完整的可扩展性来处理大数据集的,不过,有时Hive的输入数据量是是非常小的,这种情况下,为查询触发执行任务时消耗可能会比实际job的执行时间要多得多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。
用户可以通过设置hive.exec.mode.local.auto的值为true,来让Hive在适当的时候自动启动这个优化。
案例演示:
开启本地模式,并执行查询语句
set hive.exec.mode.local.auto=true;
select * from score cluster by s_id;
关闭本地模式,并执行查询语句
set hive.exec.mode.local.auto=false;
select * from score cluster by s_id;
如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会在Reduce阶段完成join,容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。
开启MapJoin参数设置:
设置自动选择Mapjoin
set hive.auto.convert.join = true;
大表小表的阈值设置(默认25M以下认为是小表):
set hive.mapjoin.smalltable.filesize=25123456;
默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。
开启Map端聚合参数设置
(1)是否在Map端进行聚合,默认为True
set hive.map.aggr = true;
(2)在Map端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000;
(3)有数据倾斜的时候进行负载均衡(默认是false)
set hive.groupby.skewindata = true;
数据量小的时候无所谓,数据量大的情况下,由于COUNT DISTINCT操作需要用一个ReduceTask来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNTDISTINCT使用先GROUP BY再COUNT的方式替换:
select count(distinct s_id) from score;
select count(s_id) from (select id from score group by s_id) a;
虽然会多用一个Job来完成,但在数据量大的情况下,这个绝对是值得的。
尽量避免笛卡尔积,即避免join的时候不加on条件,或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积。
开启动态分区参数步骤
(1)开启动态分区功能(默认true,开启)
set hive.exec.dynamic.partition=true;
(2)设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。)
set hive.exec.dynamic.partition.mode=nonstrict;
(3)在所有执行MR的节点上,最大一共可以创建多少个动态分区。
set hive.exec.max.dynamic.partitions=1000;
(4)在每个执行MR的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。
set hive.exec.max.dynamic.partitions.pernode=100;
(5)整个MR Job中,最大可以创建多少个HDFS文件。在linux系统当中,每linux用户最多可以开启1024个进程,每一个进程最多可以打开2048个文件,即持2048个文件句柄,下面这个值越大,就可以打开文件句柄越大.
set hive.exec.max.created.files=100000;
(6)当有空分区生成时,是否抛出异常。一般不需要设置。
set hive.error.on.empty.partition=false;
案例演示:
需求:将ori中的数据按照时间(如:20111231234568),插入到目标表ori_partitioned的相应分区中。
(1)准备数据原表
create table ori_partitioned(id bigint,
time bigint,
uid string,
keyword string,
url_rank int,
click_num int,
click_url string)
PARTITIONED BY (p_time bigint)
row format delimited fields terminated by '\t';
load data local inpath '/export/servers/hivedatas/small_data' into table
ori_partitioned partition (p_time='20111230000010');
load data local inpath '/export/servers/hivedatas/small_data' into table
ori_partitioned partition (p_time='20111230000011');
(2)创建目标分区表
create table ori_partitioned_target(id bigint,
time bigint,
uid string,
keyword string,
url_rank int,
click_num int,
click_url string)
PARTITIONED BY (p_time STRING)
row format delimited fields terminated by '\t';
(3)向目标分区表加载数据
如果按照之前介绍的往指定一个分区中Insert数据,那么这个需求很不容易实现。这时候就需要使用动态分区来实现。
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;
注意:在SELECT子句的最后几个字段,必须对应前面PARTITION (p_time)中指定的分区字段,包括顺序。
(4)查看分区
show partitions ori_partitioned_target;
Hive会将一个查询转化成一个或者多个阶段。这样的阶段可以是MapReduce阶段、抽样阶段、合并阶段、limit阶段。或者Hive执行过程中可能需要的其他阶段。默认情况下,Hive一次只会执行一个阶段。不过,某个特定的job可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个job的执行时间缩短。不过,如果有更多的阶段可以并行执行,那么job可能就越快完成。
通过设置参数hive.exec.parallel值为true,就可以开启并发执行。不过,在共享集群中,需要注意下,如果job中并行阶段增多,那么集群利用率就会增加。
set hive.exec.parallel = true;
当然,得是在系统资源比较空闲的时候才有优势,否则,没资源,并行也起不来。
Hive提供了一个严格模式,可以防止用户执行那些可能意向不到的不好的影响的查询。通过设置属性hive.mapred.mode值为默认是非严格模式nonstrict 。开启严格模式需要修改hive.mapred.mode值为strict,开启严格模式可以禁止3种类型的查询。
set hive.mapred.mode = strict; #开启严格模式
set hive.mapred.mode = nostrict; #开启非严格模式
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。
Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。JVM重用可以使得JVM实例在同一个job中重新使用N次。N的值可以在Hadoop的mapred-site.xml文件中进行配置。通常在10-20之间,具体多少需要根据具体业务场景测试得出。
我们也可以在hive当中通过设置JVM重用:
set mapred.job.reuse.jvm.num.tasks=10;
这个功能的缺点是,开启JVM重用将一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job中有某几个reduce task执行的时间要比其他Reducetask消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。
在分布式集群环境下,因为程序Bug(包括Hadoop本身的bug),负载不均衡或者资源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速度可能明显慢于其他任务(比如一个作业的某个任务进度只有50%,而其他所有任务已经运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生,Hadoop采用了推测执行(Speculative Execution)机制 ,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
设置开启推测执行参数:
set mapred.map.tasks.speculative.execution=true
set mapred.reduce.tasks.speculative.execution=true
set hive.mapred.reduce.tasks.speculative.execution=true;
关于调优这些推测执行变量,还很难给一个具体的建议。如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大大。