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 '\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=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文件中进行配置


  mapreduce.job.jvm.numtasks
  10
  How many tasks to run per jvm. If set to -1, there is no limit

8,推测执行

  Hadoop的mapred-site.xml文件中进行配置,默认是true


  mapreduce.map.speculative
  true
  If true, then multiple instances of some map tasks  may be executed in parallel.


  mapreduce.reduce.speculative
  true
  If true, then multiple instances of some reduce tasks  may be executed in parallel.

  不过hive本身也提供了配置项来控制reduce-side的推测执行:默认是true


    hive.mapred.reduce.tasks.speculative.execution
    true
    Whether speculative execution for reducers should be turned on. 

  不建议开启的情况:(1)任务间存在严重的负载倾斜;(2)特殊任务,比如任务向数据库中写数据。

9,压缩

  见第一章

10,explain执行计划

  利用explain查看sql的执行计划

 

你可能感兴趣的:(Hive的压缩存储和简单优化)