开启Map端输出阶段压缩

当Hive使用MR引擎时,也可以使用MR的相关压缩。

开启Map输出阶段压缩可以减少 jbo 中 map 和Reduce Task 间数据传输量。

步骤:

  1. 开启hive中间传输数据压缩功能
    1. set hive.exec.compress.intermediate=true;
  1. 开启 mapreduce 中map输出压缩功能
    1. set mapreduce.map.output.compress=true;
  1. 设置 mapreduce 中map输出数据的压缩方式
    1. set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
  1. 执行查询语句
    1. select count(ename) name from emp;

开启Reduce输出阶段压缩

当Hive将输出写入到表中时,输出内容同样可以进行压缩。属性 hive.exec.compress.output控制这个功能。用户可能需要保持默认设置文件中的默认值false,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true来开启输出结果压缩功能。

步骤:

  1. 开启Hive最终输出数据压缩功能
    1. set hive.exec.compress.output=true;
  1. 开启MapReduce最终输出数据压缩
    1. set mapreduce.output.fileoutputformat.compress=true;
  1. 设置MapReduce最终数据输出压缩方式
    1. set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
  1. 设置MapReduce最终输出压缩为块压缩(默认行压缩)
    1. set mapreduce.output.fileoutputformat.compress.type=BLOCK;

测试输出结果是否为压缩文件:

  1. insert overwrite local directory '/home/tengyer/data/distribute-result'
  2. select * from emp distribute by deptno sort by empno desc;

文件存储格式

Hive支持的存储数据的格式有:TEXTFILE、SEQUENCEFILE、ORC、PARQUET。

行存储和列存储

行存储和列存储示意图:

行存储和列存储.png

对于同样的一张表,可以有行存储和列存储两种策略。Row layout 为行存储,Column layout 为列存储。

当要查询某一行数据的时候(例如select * from emp;),行存储查询的速度比列存储更快;

当要按字段进行查询时候(例如select sal from emp;),列存储的速度比行存储更快。

而且因为每个字段的数据类型一定是相同的,所以列式存储可以针对性的设计更好的压缩算法。

在大数据应用中,一般都是对列进行统计分析,所以一般使用列存储的方式。

TextFile、SequenceFile的存储格式是基于行存储的;

Orc 和 Parquet 是基于列式的。

TextFile格式

默认的格式,数据不做压缩,磁盘开销大,数据解析开销大。

可以结合 Gzip、Bzip2 使用。但是使用 Gzip 时,Hive不会对数据进行切分,从而无法对数据进行并行操作。

创建表时指定数据的存储格式为textfile:

  1. create table test(
  2. id string,
  3. name string
  4. )
  5. row format delimited fields terminated by ','
  6. -- 指定存储格式为Textfile(默认就是Textfile
  7. stored as textfile;

Orc格式

Orc(Optimized Row Columnar)是 Hive0.11版本中引入的新的存储格式。

Orc文件类似于列存储,但又和列存储不太一样。列存储是把一列的所有数据全部放到一起,但是实际环境中,一列的数据也是非常多的,这样依然不方便。Orc会将列中的数据进行分成若干片(stripe),按片进行存储。为了能够找到该列所有数据所在的stripe,ORC还会维护一个索引来记录。

每个Orc文件由1个或多个stripe组成,每个stripe一般为 HDFS 的块大小,每一个stripe包含多条记录,这些记录按照列进行独立存储,对应到 Parquet 中的 row group概念。每个stripe里面有三部分组成,分别是Index DataRow DataStripe Footer

示意图:

orc格式.jpg

其中:

  • Index Data:一个轻量级Index,默认是每隔1万行做一个索引。这里做的索引只是记录某行的个字段在Row Data中的offset
  • Row Data:存的是具体数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个Stream来存储
  • Stripe Footer:存的是各个Stream的类型、长度等信息

每个文件有一个 File Footer,连存的是每个Stripe的行数,每个Column的数据类型信息;每个文件的尾部是一个PostScript,里面记录了整个文件的压缩类型以及File Footer的长度信息等。

在读取文件时,会seek到文件尾部读取PostScript,从里面解析到File Footer长度,再读File Footer,从里面解析到各个Stripe信息,再读各个Stripe。即从后往前读。

创建表时指定数据的存储格式为orc:

  1. create table test(
  2. id string,
  3. name string
  4. )
  5. row format delimited fields terminated by ','
  6. -- 指定存储格式为orc
  7. stored as orc
  8. -- 设置orc不使用压缩(orc默认使用了压缩,可以指定为不使用压缩)
  9. tblproperties("orc.compress"="NONE");

Parquet格式

Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此 Parquet格式文件是自解析的。

  • Row Group:行组。每一个行组包括一定的行数,在一个 hdfs 文件中至少存储一个行组,类似 ORC 的stripe概念
  • Column Chunk:列块。在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩
  • Page:页。每个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。

通常情况下,在存储 Parquet 数据的时候会按照block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。

示意图:

parquet.png

创建表时指定数据的存储格式为parquet:

  1. create table test(
  2. id string,
  3. name string
  4. )
  5. row format delimited fields terminated by ','
  6. -- 指定存储格式为parquet
  7. stored as parquet;

几种格式的对比

在都不启用压缩的情况下:

  • 几种格式的等量数据最终文件大小为:Orc格式的文件最小,其次是 Parquet,最大的是TextFile。
  • 几种格式的查询速度相差不多

存储和压缩结合

ORC存储方式,使用压缩时可以配置的相关属性(tblproperties):

属性名 默认值 备注
orc.bloom.filter.columns “” comma separated list of column names for which bloom filter should be created
orc.bloom.filter.fpp 0.05 false positive probability for bloom filter (must >0.0 and <1.0)
orc.compress ZLIB high level compression (one of NONE, ZLIB, SNAPPY)
orc.compress.size 262,144 number of bytes in each compression chunk
orc.create.index true whether to create row indexes
orc.row.index.stride 10,000 number of rows between index entries (must be >= 1000)
orc.stripe.size 67,108,864 number of bytes in each stripe

例如:使用ZLIB压缩:

  1. create table test(
  2. id string,
  3. name string
  4. )
  5. row format delimited fields terminated by ','
  6. -- 指定存储格式为orc
  7. stored as orc
  8. -- 设置orc使用ZLIB压缩,也可以使用SNAPPY等其他压缩算法
  9. tblproperties("orc.compress"="ZLIB");

Parquet使用SNAPPY压缩示例:

  1. create table test(
  2. id string,
  3. name string
  4. )
  5. row format delimited fields terminated by ','
  6. -- 指定存储格式为parquet
  7. stored as parquet
  8. -- 设置parquet使用SNAPPY压缩
  9. tblproperties("parquet.compression"="SANPPY");