1 SQL优化
1)列裁剪
Hive在读数据的时候,可以只读取查询中所需要用到的列,而忽略其他列。
2)分区裁剪
可以在查询的过程中减少不必要的分区。
3)不同数据类型关联产生的倾斜问题
不同数据类型id的关联会产生数据倾斜问题。
4)COUNT(DISTINCT)
计算uv的时候,经常会用到COUNT(DISTINCT),但在数据比较倾斜的时候COUNT(DISTINCT)会比较慢。这时可以尝试用GROUP BY改写代码计算uv。数据量小的时候无所谓,数据量大的情况下,由于COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换
5)JOIN操作
将条目少的表/子查询放在Join操作符的左边。
6)GROUP BY操作
Map端部分聚合,有数据倾斜时进行负载均衡
7)优化in/exists语句
hive支持in/exists操作,推荐使用hive的一个高效替代方案:left semi join
8)排序选择
cluster by: 对同一字段分桶并排序,不能和sort by连用;
distribute by + sort by: 分桶,保证同一字段值只存在一个结果文件当中,结合sort by 保证每个reduceTask结果有序;
sort by: 单机排序,单个reduce结果有序
order by:全局排序,缺陷是只能使用一个reduce
2 设置合理的map reduce的task数量
1)map阶段优化
mapred.min.split.size: 指的是数据的最小分割单元大小;min的默认值是1B
mapred.max.split.size: 指的是数据的最大分割单元大小;max的默认值是256MB
通过调整max可以起到调整map数的作用,减小max可以增加map数,增大max可以减少map数。
需要提醒的是,直接调整mapred.map.tasks这个参数是没有效果的。
set mapred.max.split.size=100000000;(约95M)
set mapred.min.split.size.per.node=100000000;(约95M)
set mapred.min.split.size.per.rack=100000000;(约95M)
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
2) reduce阶段优化
如果设置了mapred.reduce.tasks/mapreduce.job.reduces参数,那么Hive会直接使用它的值作为Reduce的个数;如果mapred.reduce.tasks/mapreduce.job.reduces的值没有设置(也就是-1),那么Hive会根据输入文件的大小估算出Reduce的个数。根据输入文件估算Reduce的个数可能未必很准确,因为Reduce的输入是Map的输出,而Map的输出可能会比输入要小,所以最准确的数根据Map的输出估算Reduce的个数。
1. Hive自己如何确定reduce数:
reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定:
hive.exec.reducers.bytes.per.reducer(每个reduce任务处理的数据量,默认为1000^3=1G)
hive.exec.reducers.max(每个任务最大的reduce数,默认为999)
计算reducer数的公式很简单N=min(参数2,总输入数据量/参数1)
即,如果reduce的输入(map的输出)总大小不超过1G,那么只会有一个reduce任务;
2. 调整reduce个数方法一:
调整hive.exec.reducers.bytes.per.reducer参数的值;
set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
select pt, count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
3. 调整reduce个数方法二:
set mapred.reduce.tasks=15;
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
这次有15个reduce
4. reduce个数并不是越多越好;
同map一样,启动和初始化reduce也会消耗时间和资源;
另外,有多少个reduce,就会有个多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;
5. 什么情况下只有一个reduce;
很多时候你会发现任务中不管数据量多大,不管你有没有调整reduce个数的参数,任务中一直都只有一个reduce任务;其实只有一个reduce任务的情况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的情况外,还有以下原因:
- 没有group by的汇总,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’ group by pt; 写成select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’; 这点非常常见,希望大家尽量改写。
- 用了Order by
- 有笛卡尔积。
注意:在设置reduce个数的时候也需要考虑这两个原则:使大数据量利用合适的reduce数;是单个reduce任务处理合适的数据量;
3 小文件合并优化
文件数目小,容易在文件存储端造成瓶颈,给HDFS带来压力,影响处理效率。对此,可以通过合并Map和Reduce的结果文件来消除这样的影响。
用于设置合并的参数有:
- 是否合并Map输出文件:hive.merge.mapfiles=true(默认值为true)
- 是否合并Reduce端输出文件:hive.merge.mapredfiles=false(默认值为false)
- 合并文件的大小:hive.merge.size.per.task=256*1000*1000(默认值为256000000)
Hive优化之小文件问题及其解决方案:
小文件是如何产生的:
- 动态分区插入数据,产生大量的小文件,从而导致map数量剧增;
- reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的);
- 数据源本身就包含大量的小文件。
小文件问题的影响:
- 从Hive的角度看,小文件会开很多map,一个map开一个JVM去执行,所以这些任务的初始化,启动,执行会浪费大量的资源,严重影响性能。
- 在HDFS中,每个小文件对象约占150byte,如果小文件过多会占用大量内存。这样NameNode内存容量严重制约了集群的扩展。
小文件问题的解决方案:
从小文件产生的途径就可以从源头上控制小文件数量,方法如下:
- 使用Sequencefile作为表存储格式,不要用textfile,在一定程度上可以减少小文件;
- 减少reduce的数量(可以使用参数进行控制);
- 少用动态分区,用时记得按distribute by分区;
对于已有的小文件,我们可以通过以下几种方案解决:
- 使用hadoop archive命令把小文件进行归档;
- 重建表,建表时减少reduce数量;
- 通过参数进行调节,设置map/reduce端的相关参数,如下:
//每个Map最大输入大小(这个值决定了合并后文件的数量)
set mapred.max.split.size=256000000;
//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000;
//执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
设置map输出和reduce输出进行合并的相关参数:
[java] view plain copy
//设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
//设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
//设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000
4 存储格式
可以使用列裁剪,分区裁剪,orc,parquet等这些列式存储格式,因为列式存储的表,每一列的数据在物理上是存储在一起的,Hive查询时会只遍历需要列数据,大大减少处理的数据量。
Hive支持ORCfile,这是一种新的表格存储格式,通过诸如谓词下推,压缩等技术来提高执行速度提升。对于每个HIVE表使用ORCfile应该是一件容易的事情,并且对于获得HIVE查询的快速响应时间非常有益。
举一个例子,考虑两个大表A和B(作为文本存储,其中一些列未在此处指定,即行式存储的缺点)以及一个简单的查询,如:
SELECT A.customerID,A.name,A.age,A.address join B.role,B.department,B.salary ON A.customerID=B.customerID;
此查询可能需要很长时间才能执行,因为表A和B都以TEXT形式存储,进行全表扫描。
将这些表格转换为ORCFile格式通常会显着减少查询时间;
ORC支持压缩存储(使用ZLIB或如上所示使用SNAPPY),但也支持未压缩的存储。
5 压缩格式
大数据场景下存储格式压缩格式尤为关键,可以提升计算速度,减少存储空间,降低网络io,磁盘io,所以要选择合适的压缩格式和存储格式。
压缩的原因
Hive最终是转为MapReduce程序来执行的,而MapReduce的性能瓶颈在于网络IO和磁盘IO,要解决性能瓶颈,最主要的是减少数据量,对数据进行压缩是个好的方式。压缩虽然是减少了数据量,但是压缩过程要消耗CPU的,但是在Hadoop中,往往性能瓶颈不在于CPU,CPU压力并不大,所以压缩充分利用了比较空闲的CPU。
常用压缩方法对比
压缩方式的选择
压缩比率,压缩解压缩速度,是否支持Split
压缩使用
Job输出文件按照block以Gzip的方式进行压缩:
set mapreduce.output.fileoutputformat.compress=true // 默认值是 false
set mapreduce.output.fileoutputformat.compress.type=BLOCK // 默认值是 Record
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec // 默认值是 org.apache.hadoop.io.compress.DefaultCodec
Map输出结果也以Gzip进行压缩:
set mapred.map.output.compress=true
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.GzipCodec // 默认值是 org.apache.hadoop.io.compress.DefaultCodec
对Hive输出结果和中间都进行压缩:
set hive.exec.compress.output=true // 默认值是 false,不压缩
set hive.exec.compress.intermediate=true // 默认值是 false,为 true 时 MR 设置的压缩才启用
6 引擎的选择
hive引擎:Map-Reduce引擎,Apache Tez执行引擎,Hive On Spark引擎。最好的是Hive On Spark引擎。