一、Hive的压缩和存储
1,MapReduce支持的压缩编码
压缩格式 |
工具 |
算法 |
文件扩展名 |
是否可切分 |
对应的编码/解码器 |
DEFLATE |
无 |
DEFLATE |
.deflate |
否 |
org.apache.hadoop.io.compress.DefaultCodec |
Gzip |
gzip |
DEFLATE |
.gz |
否 |
org.apache.hadoop.io.compress.GzipCodec |
bzip2 |
bzip2 |
bzip2 |
.bz2 |
是 |
org.apache.hadoop.io.compress.BZip2Codec |
LZO |
lzop |
LZO |
.lzo |
是 |
com.hadoop.compression.lzo.LzopCodec |
Snappy |
无 |
Snappy |
.snappy |
否 |
org.apache.hadoop.io.compress.SnappyCodec |
2,文件压缩格式:
TEXTFILE和SEQUENCEFILE的存储格式都是基于行式存储的;
ORC和PARQUET是基于列式存储的。
a>TextFile格式:
默认格式,数据不做压缩,磁盘开销大,数据解析开销大。可结合Gzip、Bzip2使用,但使用Gzip这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。
b>Orc格式:
Hive 0.11版里引入的新的存储格式,数据按行分块 每块按照列存储 ,压缩快 快速列存取,效率比rcfile高,是rcfile的改良版本,相比RC能够更好的压缩,能够更快的查询,但还是不支持模式演进。
c>parquent格式:
Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。
在实际的项目开发当中,hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy,lzo。
二、Hive的企业级调优:
1,Fetch抓取:
默认开启。Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。(hive-default.xml.template文件中hive.fetch.task.conversion默认是more)例如:SELECT * FROM person;在这种情况下,Hive可以简单地读取person对应的存储目录下的文件,然后输出查询结果到控制台。
2,本地模式:
大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务消耗的时间可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。
set hive.exec.mode.local.auto=true; //开启本地mr
//设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;
//设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;
3,表的优化:
a>大小表的join:
新版的hive已经对小表JOIN大表和大表JOIN小表进行了优化。小表放在左边和右边已经没有明显区别。
b>大表join大表:
空key过滤:空key对应的数据无意义
select n.* from (select * from nullidtable where id is not null ) n left join ori o on n.id = o.id;
空key转换:空key对应的数据还是有意义,需要保留
#为空key赋予随机值,在进入reduce的时候防止空key太多而造成数据倾斜
select
n.* from nullidtable n full join ori o on
case when n.id is null then concat('hive', rand()) else n.id end = o.id;
c>MapJoin(小表join大表)
#默认开启
set hive.auto.convert.join = true;
#大表小表的阈值设置(默认25M以下认为是小表)可以调整
set hive.mapjoin.smalltable.filesize=25000000;
d>group by:
默认情况下,Map阶段同一个key数据分发到同一个reduce,当一个key的数据过大时就会出现数据倾斜。
#是否在Map端进行聚合,默认为True
set hive.map.aggr = true
#在Map端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000
#有数据倾斜的时候进行负载均衡(默认是false)
set hive.groupby.skewindata = true
当设置为负载均衡之后,生成的计划会有两个MR的job。第一个MRjob,Map的输出结果可能会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MRjob再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
e>Count(distinct)去重统计
数据量小的时候无所谓,数据量大的情况下,由于COUNT DISTINCT的全聚合操作,即使设定了reduce task个数,set mapred.reduce.tasks=100;hive也只会启动一个reducer,这就造成一个Reduce处理的数据量太大,导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换:
#设置reduce个数为5
set mapreduce.job.reduces = 5;
#采用distinct去重
select count(distinct id) from bigtable;
#采用group by去重
select count(id) from (select id from bigtable group by id) a;
虽然会多用一个Job来完成,但在数据量大的情况下,这个绝对是值得的。
f>笛卡尔积
尽量避免笛卡尔积,join的时候不加on条件,或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积。
g>行列过滤
列处理:在select中,只拿需要的列,如果有,尽量使用分区过滤,少用select *。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在where后面,那么就会先全表关联,之后再过滤。
#先关联再过滤
select o.id from bigtable b join ori o on o.id = b.id
#先过滤再关联
select b.id from bigtable b
join (select id from ori where id <= 10 ) o on b.id = o.id;
h>动态分区
(1)开启动态分区功能(默认true,开启)
set hive.exec.dynamic.partition=true
(2)设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。)
set hive.exec.dynamic.partition.mode=nonstrict
(3)在所有执行MR的节点上,最大一共可以创建多少个动态分区。默认1000
set hive.exec.max.dynamic.partitions=1000
(4)在每个执行MR的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。
set hive.exec.max.dynamic.partitions.pernode=100
(5)整个MR Job中,最大可以创建多少个HDFS文件。默认100000
set hive.exec.max.created.files=100000
(6)当有空分区生成时,是否抛出异常。一般不需要设置。默认false
set hive.error.on.empty.partition=false
i>分区
分区表实际上就是对应一个HDFS文件系统上的独立的文件夹,该文件夹下是该分区所有的数据文件。Hive中的分区就是分目录,把一个大的数据集根据业务需要分割成小的数据集。在查询时通过WHERE子句中的表达式选择查询所需要的指定的分区,这样的查询效率会提高很多。
j>分桶
分区提供一个隔离数据和优化查询的便利方式。不过,并非所有的数据集都可形成合理的分区。对于一张表或者分区,Hive 可以进一步组织成桶,也就是更为细粒度的数据范围划分。分区针对的是数据的存储路径;分桶针对的是数据文件。
#创建分桶表
create table stu_buck(id int, name string)
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\t';
#创建中间表
create table stu(id int, name string) row format delimited fields terminated by '\t';
#导入数据到中间表
load data local inpath '/opt/module/datas/student.txt' into table stu;
#开启分桶
set hive.enforce.bucketing=true;
#设置reduce数量为-1
set mapreduce.job.reduces=-1;
#向分桶表中导入数据
insert into table stu_buck select id, name from stu;
4,合理设置Map的数量和Reduce的数量
a>复杂文件增加map数量
增加map的方法为:根据computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M公式,调整maxSize最大值。
让maxSize最大值低于blocksize就可以增加map的个数。
set mapreduce.input.fileinputformat.split.maxsize=100;
b>小文件进行合并
#设置为CombineHiveInputFormat合并小文件
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
#在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;
c>合理设置reduce的个数
1.调整reduce个数方法一
(1)每个Reduce处理的数据量默认是256MB
hive.exec.reducers.bytes.per.reducer=256000000
(2)每个任务最大的reduce数,默认为1009
hive.exec.reducers.max=1009
(3)计算reducer数的公式
N=min(参数2,总输入数据量/参数1)
2.调整reduce个数方法二
在hadoop的mapred-default.xml文件中修改
设置每个job的Reduce个数
set mapreduce.job.reduces = 15;
3.reduce个数并不是越多越好
1)过多的启动和初始化reduce也会消耗时间和资源;
2)另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;
在设置reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的reduce数;使单个reduce任务处理数据量大小要合适;
5,并行执行
Hive会将一个查询转化成一个或者多个阶段。这样的阶段可以是MapReduce阶段、抽样阶段、合并阶段、limit阶段。或者Hive执行过程中可能需要的其他阶段。默认情况下,Hive一次只会执行一个阶段。不过,某个特定的job可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个job的执行时间缩短。不过,如果有更多的阶段可以并行执行,那么job可能就越快完成。
set hive.exec.parallel=true; //打开任务并行执行
set hive.exec.parallel.thread.number=16; //同一个sql允许最大并行度,默认为8。
在共享集群中,需要注意下,如果job中并行阶段增多,那么集群利用率就会增加。当然,得是在系统资源比较空闲的时候才有优势,否则,没资源,并行也起不来。
6,严格模式
通过设置属性hive.mapred.mode值为默认是非严格模式nonstrict 。开启严格模式需要修改hive.mapred.mode值为strict,开启严格模式可以禁止3种类型的查询。
1)对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。(就是用户不允许扫描所有分区)
2)对于使用了order by语句的查询,要求必须使用limit语句。 因为order by为了执行排序过程会将所有的结果数据分发到同一个Reducer中进行处理,强制要求用户增加这个LIMIT语句可以防止Reducer额外执行很长一段时间。
3)限制笛卡尔积的查询。
7,JVM重用
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。
在Hadoop的mapred-site.xml文件中进行配置
<property>
<name>mapreduce.job.jvm.numtasks</name>
<value>10</value>
<description>How many tasks to run per jvm. If set to -1, there is no limit</description>
</property>
8,推测执行
Hadoop的mapred-site.xml文件中进行配置,默认是true
<property>
<name>mapreduce.map.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some map tasks may be executed in parallel.</description>
</property>
<property>
<name>mapreduce.reduce.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some reduce tasks may be executed in parallel.</description>
</property>
不过hive本身也提供了配置项来控制reduce-side的推测执行:默认是true
<property>
<name>hive.mapred.reduce.tasks.speculative.execution</name>
<value>true</value>
<description>Whether speculative execution for reducers should be turned on. </description>
</property>
不建议开启的情况:(1)任务间存在严重的负载倾斜;(2)特殊任务,比如任务向数据库中写数据。
9,压缩
见第一章
10,explain执行计划
利用explain查看sql的执行计划
来源:oschina
链接:https://my.oschina.net/u/4286012/blog/4303637