大数据之Hive简介第二部分

大数据之Hive简介第二部分

  • 九、Hive的调优
    • 执行计划
    • Fetch抓取
    • 本地模式
    • 表的优化
    • Group by
    • count(distinct)
    • 行列过滤
    • 合理设置 Map 及 Reduce 数
    • 并行执行
    • 严格模式
    • 其他优化
  • 十、Hive的Tez引擎

九、Hive的调优

执行计划

基本语法

EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query

查询执行语句的执行计划:

hive (abc)> explain select * from student;
# 详细查看执行语句的执行计划
hive (abc)> explain extended select * from emp;

执行计划里面会有:Fetch Operator的字样,该执行计划没有经过MR程序。

# 经过MR程序的语句:
hive (abc)> explain select deptno, avg(sal) avg_sal from emp group by deptno;
# 详细查看执行语句的执行计划
hive (default)> explain extended select deptno, avg(sal) avg_sal from emp group by deptno;

执行计划里面会有:Map Operator Tree:、Reduce Operator Tree:、Select Operator、File Output Operator、Fetch Operator等,说明经过了MR程序,以上都是MR程序的解析说明。

Fetch抓取

Fetch抓取,Hive中对某些情况的查询可以不必使用MapReduce计算。hive-default.xml.template 文件中 hive.fetch.task.conversion 默认值是 more,老版本 hive 默认是 minimal。将该属性修改为 more 以后,在全局查找、字段查找、limit 查找等都不走 mapreduce,提高效率。

hive (default)> set hive.fetch.task.conversion=more; 
hive (default)> select * from emp;
hive (default)> select ename from emp;
hive (default)> select ename from emp limit 3;

本地模式

大多数的 Hadoop Job 是需要 Hadoop 提供的完整的可扩展性来处理大数据集的。不过,有时 Hive 的输入数据量是非常小的。在这种情况下,为查询触发执行任务消耗的时间可能 会比实际 job 的执行时间要多的多。对于大多数这种情况,Hive 可以通过本地模式在单台机 器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。
用户可以通过设置 hive.exec.mode.local.auto 的值为 true,来让 Hive 在适当的时候自动 启动这个优化。

# 开启本地 mr
set hive.exec.mode.local.auto=true;
# 设置 local mr 的最大输入数据量,当输入数据量小于这个值时采用 local mr 的方式,默认 为 134217728,即 128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;  # 50M是 52428800
# 设置 local mr 的最大输入文件个数,当输入文件个数小
set hive.exec.mode.local.auto.input.files.max=10;

简单示例:

# 可以查看本地模式的开启状态
hive (default)> set hive.exec.mode.local.auto;
# 查询语句
hive (default)> select count(*) from emp group by deptno;
# 开启本地模式,并执行查询语句
hive (default)> set hive.exec.mode.local.auto=true;
hive (default)> select count(*) from emp group by deptno;

可以对比是否开启本地模式查询的速度,验证是否需要开启本地模式。同时开启本地模式后,MR任务都是会显示job的jobid带有local字样。如:Ended job = job_loca11784683429_0001。

表的优化

(1)小表Join大表
其实跟SQL的时候一样,将小表放在左边。Hive这里的话,之前是可以将放在左边的小表预先加载进内存,但现在新版本的优化做的比较好了,所有其实小表join大表跟大表join小表的情况差不多,建议还是习惯将小表放在左边。
尽量避免笛卡尔积,join 的时候不加 on 条件,或者无效的 on 条件时,Hive 只能使用 1 个reducer 来完成笛卡尔积。
(2)异常值处理
null值处理:
当null值过多的时候,一般可以通过条件语句,将null剔除掉;二是能尽量在join之前剔除,都在join之前剔除,因为剔除之后去join的数据就会变少,能提高效率。例如以下,显然第一种的效率比第二种要高。

# 第一种
select a.*,b.*
from (select * from tableA where id is not null) a
left join (select * from tableB where id is not null) b
on a.id = b.id;
# 第二种
select a.*,b.* from tableA a
left join tableB b on a.id = b.id
where a.id is not null and b.id is not null;

null值转换:
当null是为正常值,且是必须要的时候,又因为null数量很多,为了不让其只进入一个reduce,这里通过给null附加随机值的做法,让null能均匀的分散到Reduce中去处理。

# 设置 5 个 reduce 个数
set mapreduce.job.reduces = 5; 	
# JOIN 两张表
insert overwrite table jointable
select n.* from nullidtable n full join bigtable o on nvl(n.id,rand()) = o.id;

SMB(Sort Merge Bucket join)分桶join:
分桶Join的思路就是按照两个表的连接关键字段分桶,因为分桶是按照哈希分配的,所有相同的id一定会分到序号相同的桶,也就是将大表分成一一对应的小表,再join,最后在汇总结果即可。先按照桶把两个表的id分成4份,4个桶就相当于4个小表,join之后再在reduce端合并结果即可。这个就是分桶表的思路。可以明显的提高效率从2小时到10分钟。

# 设置分桶表参数
set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true; 
set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;

创建分桶表,桶的数量不要超过可用CPU的核数,分桶的字段按照join的字段:

# 创建分桶表1
create table bigtable_buck1( id bigint,
			t bigint, uid string,
			keyword string, url_rank int, click_num int, click_url string)
clustered by(id) sorted by(id) into 6 buckets
row format delimited fields terminated by '\t';
load data local inpath '/opt/module/data/bigtable' into table bigtable_buck1;
# 创建分桶表2
create table bigtable_buck2( id bigint,
t bigint, uid string,
keyword string, url_rank int, click_num int, click_url string)
clustered by(id) sorted by(id) into 6 buckets
row format delimited fields terminated by '\t';
load data local inpath '/opt/module/data/bigtable' into table bigtable_buck2;

分桶表查询测试:

insert overwrite table jointable
select b.id, b.t, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url from bigtable_buck1 s
join bigtable_buck2 b on b.id = s.id;

Group by

group by 分组,同一个key会在同一个reduce里面执行,group by容易发生数据倾斜很多操作其实可以先在Map端先完成,到了Reduce端再合并。

# (1)是否在 Map 端进行聚合,默认为 True
set hive.map.aggr = true 	
#(2)在 Map 端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000 
#(3)有数据倾斜的时候进行负载均衡(默认是 false)
set hive.groupby.skewindata = true 

当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出 结果会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果 是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二 个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证 相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。也是当数据量大的时候才会考虑使用,同时也可以考虑使用combiner的操作先提前合并。

count(distinct)

数据量小的时候无所谓,数据量大的情况下,由于 COUNT DISTINCT 操作需要用一个 Reduce Task 来完成,这一个 Reduce 需要处理的数据量太大,就会导致整个 Job 很难完成, 一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替换,但是需要注意 group by 造成 的数据倾斜问题.

# group by 使用语句如下:
hive (default)> select count(id) from (select id from bigtable group by id) a;

行列过滤

列处理:在 SELECT 中,只拿需要的列,如果有分区,尽量使用分区过滤,少用 SELECT*。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在 Where 后面,那么就会先全表关联,之后再过滤。所以应该在表里面先用条件筛选,再进行join等其他操作。
(1)测试先关联两张表,再用 where 条件过滤

hive (default)> select o.id from bigtable b join bigtable o on o.id = b.id where o.id <= 10;
Time taken: 34.406 seconds, Fetched: 100 row(s)

(2)通过子查询后,再关联表

hive (default)> select b.id from bigtable b join (select id from bigtable where id <= 10) o on b.id = o.id;
Time taken: 30.058 seconds, Fetched: 100 row(s)

合理设置 Map 及 Reduce 数

通常情况下,作业会通过 input 的目录产生一个或者多个 map 任务。 主要的决定因素有:input 的文件总个数,input 的文件大小,集群设置的文件块大小等。
(1)复杂文件需增加Map数增加 map 的方法为:根据
computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M 公式,
调整 maxSize 最大值。让 maxSize 最大值低于 blocksize 就可以增加 map 的个数。
(2)小文件需要进行合并
CombineHiveInputFormat 具有对小文件进行合 并的功能(系统默认的格式)。HiveInputFormat 没有对小文件合并功能。但是系统默认帮我们设置了CombineHiveInputFormat的形式,所以系统默认是会合并小文件的。

set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

在 Map-Reduce 的任务结束时合并小文件的设置:

# 在 map-only 任务结束时合并小文件,默认 true
SET hive.merge.mapfiles = true; 	
# 在 map-reduce 任务结束时合并小文件,默认 false
SET hive.merge.mapredfiles = true; 	
# 合并文件的大小,默认 256M
SET hive.merge.size.per.task = 268435456; 	 
# 当输出文件的平均大小小于该值时,启动一个独立的 map-reduce 任务进行文件 merge  
SET hive.merge.smallfiles.avgsize = 16777216;  -- 16*1024*1024

(3)合理设置Reduce的数量
方法一:

# 每个 Reduce 处理的数据量默认是 256MB
hive.exec.reducers.bytes.per.reducer=256000000 	
# 每个任务最大的 reduce 数,默认为 1009
hive.exec.reducers.max=1009 	
# 计算 reducer 数的公式
N=min(参数 2,总输入数据量/参数 1) 	

方法二:
在 hadoop 的 mapred-default.xml 文件中修改 设置每个 job 的 Reduce 个数

set mapreduce.job.reduces = 15; 

Reduce 个数并不是越多越好,过多的启动和初始化 reduce 也会消耗时间和资源;另外,有多少个 reduce,就会有多少个输出文件,如果生成了很多个小文件,那 么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;在设置 reduce 个数的时候也需要考虑这两个原则:处理大数据量利用合适的 reduce 数; 使单个 reduce 任务处理数据量大小要合适;

并行执行

Hive 会将一个查询转化成一个或者多个阶段。这样的阶段可以是 MapReduce 阶段、抽样阶段、合并阶段、limit 阶段。或者 Hive 执行过程中可能需要的其他阶段。默认情况下,Hive 一次只会执行一个阶段。不过,某个特定的 job 可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个 job 的执行 时间缩短。

# 打开任务并行执行
set hive.exec.parallel=true;
# 同一个 sql 允许最大并行度,默认为8。
set hive.exec.parallel.thread.number=16; 

严格模式

Hive可以通过设置安全防止一些危险的操作。
(1)分区表不使用分区过滤

hive.strict.checks.no.partition.filter
set hive.strict.checks.no.partition.filter = true;

将 hive.strict.checks.no.partition.filter 设置为 true 时,对于分区表,除非 where 语句中含 有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。

(2)使用 order by 没有 limit 过滤

set hive.strict.checks.orderby.no.limit = true;

将 hive.strict.checks.orderby.no.limit 设置为 true 时,对于使用了 order by 语句的查询,要求必须使用 limit 语句。因为 order by 为了执行排序过程会将所有的结果数据分发到同一个 Reducer 中进行处理,强制要求用户增加这个 LIMIT 语句可以防止 Reducer 额外执行很长一段时间。

(3)笛卡尔积

hive.strict.checks.cartesian.product
set hive.strice.checks.cartesian.product = true;

将 hive.strict.checks.cartesian.product 设置为 true 时,会限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行 JOIN 查询的时候不使用 ON 语句而是使用 where 语 句,这样关系数据库的执行优化器就可以高效地将 WHERE 语句转化成那个 ON 语句。不幸 的是,Hive 并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情 况。

其他优化

JVM重用:

  • 多个小文件的时候可以使用,每个线程会开一个JVM,可以多个小文件共用一个JVM,这样可以节省JVM打开和关闭的时间。

数据压缩解压缩

  • 就是上面说的数据量大的时候,可以考虑节省IO的时间,开启数据的压缩。

十、Hive的Tez引擎

Tez 是一个 Hive 的运行引擎,性能优于 MR。Tez 可以将多个有依赖的作业转换为一个作业,这样只需写一次 HDFS,且中间节点较少, 从而大大提升作业的计算性能。
Tez的安装步骤如下:
(1)将 tez 安装包拷贝到集群,并解压 tar 包

[mrlin@hadoop102 software]$ mkdir /opt/module/tez 
[mrlin@hadoop102 software]$ tar -zxvf /opt/software/tez-0.10.1- SNAPSHOT-minimal.tar.gz -C /opt/module/tez

(2)上传 tez 依赖到 HDFS

[mrlin@hadoop102 software]$ hadoop fs -mkdir /tez
[mrlin@hadoop102 software]$ hadoop fs -put /opt/software/tez-0.10.1- SNAPSHOT.tar.gz /tez

(3)新建 tez-site.xml

[mrlin@hadoop102 software]$ vim $HADOOP_HOME/etc/hadoop/tez-site.xml 	

添加如下配置内容:



<configuration>
	<property>
		<name>tez.lib.urisname>
		<value>${fs.defaultFS}/tez/tez-0.10.1-SNAPSHOT.tar.gzvalue>
	property>
	<property>
		<name>tez.use.cluster.hadoop-libsname>
		<value>truevalue>
	property>
	<property>
		<name>tez.am.resource.memory.mbname>
		<value>1024value>
	property>
	<property>
		<name>tez.am.resource.cpu.vcoresname>
		<value>1value>
	property>
	<property>
		<name>tez.container.max.java.heap.fractionname>
		<value>0.4value>
	property>
	<property>
		<name>tez.task.resource.memory.mbname>
		<value>1024value>
	property>
	<property>
		<name>tez.task.resource.cpu.vcoresname>
		<value>1value>
	property>
configuration>

(4)修改 Hadoop 环境变量

[mrlin@hadoop102 software]$ vim $HADOOP_HOME/etc/hadoop/shellprofile.d/tez.sh

添加 Tez 的 Jar 包相关信息

hadoop_add_profile tez function _tez_hadoop_classpath
{
	hadoop_add_classpath "$HADOOP_HOME/etc/hadoop" after 
	hadoop_add_classpath "/opt/module/tez/*" after 
	hadoop_add_classpath "/opt/module/tez/lib/*" after
}

5)修改 Hive 的计算引擎

[mrlin@hadoop102 software]$ vim $HIVE_HOME/conf/hive-site.xml 	

添加如下配置信息:

<property>
	<name>hive.execution.enginename>
	<value>tezvalue>
property>
<property>
	<name>hive.tez.container.sizename>
	<value>1024value>
property>

(6)解决日志 Jar 包冲突

[mrlin@hadoop102 software]$ rm /opt/module/tez/lib/slf4j-log4j12- 1.7.10.jar

(7)重新执行查询语句即可。

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