Hive的压缩存储和简单优化

Posted bbgs-xc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hive的压缩存储和简单优化相关的知识,希望对你有一定的参考价值。

一、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只能使用1reducer来完成笛卡尔积

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=true2)设置为非严格模式(动态分区的模式,默认strict,表示必须指定至少一个分区为静态分区,nonstrict模式表示允许所有的分区字段都可以使用动态分区。)
set hive.exec.dynamic.partition.mode=nonstrict
(3)在所有执行MR的节点上,最大一共可以创建多少个动态分区。默认1000
set hive.exec.max.dynamic.partitions=10004)在每个执行MR的节点上,最大可以创建多少个动态分区。该参数需要根据实际的数据来设定。比如:源数据中包含了一年的数据,即day字段有365个值,那么该参数就需要设置成大于365,如果使用默认值100,则会报错。
set hive.exec.max.dynamic.partitions.pernode=1005)整个MR Job中,最大可以创建多少个HDFS文件。默认100000
set hive.exec.max.created.files=1000006)当有空分区生成时,是否抛出异常。一般不需要设置。默认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 ‘	‘;
#创建中间表
create table stu(id int, name string) row format delimited fields terminated by ‘ ‘;
#导入数据到中间表
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=2560000002)每个任务最大的reduce数,默认为1009
hive.exec.reducers.max=10093)计算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的执行计划

 

以上是关于Hive的压缩存储和简单优化的主要内容,如果未能解决你的问题,请参考以下文章

大数据技术之_08_Hive学习_04_压缩和存储(Hive高级)+ 企业级调优(Hive优化)

「Hive进阶篇」详解存储格式及压缩方式

大数据开发工程师Hive(Hive如何进行优化)

技术干货Hive实践分享之存储和压缩的坑

关于hive当中表的存储和压缩方式总结

Spark SQL优化之路——Hive篇