Hive 之 压缩和存储

一、 压缩

1.1 MR 支持的压缩编码

压缩格式 工具 算法 文件扩展名 是否可切分
DEFAULT DEFAULT .deflate
Gzip gzip DEFAULT .gz
bzip2 bzip2 bzip2 .bz2
LZO lzop LZO .lzo
Snappy Snappy .snappy

为了支持多种压缩/解压缩算法, Hadoop 引入了编码/解码器,如下表所示:

压缩格式 对应的编码/解码器
DEFLATE org.apache.hadoop.io.compress.DefaultCodec
gzip org.apache.hadoop.io.compress.GzipCodec
bzip2 org.apache.hadoop.io.compress.BZip2Codec
LZO com.hadoop.compression.lzo.LzopCodec
Snappy org.apache.hadoop.io.compress.SnappyCodec

压缩性能比较:

压缩算法 原始文件大小 压缩文件大小 压缩速度 解压速度
gzip 8.3GB 1.8GB 17.5MB/s 58MB/s
bzip2 8.3GB 1.1GB 2.4MB/s 9.5MB/s
LZO 8.3GB 2.9GB 49.3MB/s 74.6MB/s

http://google.github.io/snappy/
On a single core of a Core i7 processor in 64-bit mode, Snappy compresses at about 250 MB/sec or more and decompresses at about 500 MB/sec or more

1.2 压缩参数配置

参数 默认值 阶段 建议
io.compression.codecs (在 core-site.xml 中配置) org.apache.hadoop.io.compress.DefaultCodec,
org.apache.hadoop.io.compress.GzipCodec,
org.apache.hadoop.io.compress.BZip2Codec,
org.apache.hadoop.io.compress.Lz4Codec
输入压缩 Hadoop 使用文件扩展名判断是否支持某种编解码器
mapreduce.map.output.compress false mapper 输出 这个参数设为 true 启用压缩
mapreduce.map.output.compress.codec org.apache.hadoop.io.compress.DefaultCodec mapper 输出 使用 LZO、LZ4 或 snappy 编解码器在此阶段压缩数据
mapreduce.output.fileoutputformat.compress false reducer 输出 这个参数设为 true 启用压缩
mapreduce.output.fileoutputformat.compress.codec org.apache.hadoop.io.compress.DefaultCodec reducer 输出 使用标准工具或者编解码器,如 gzip 和 bzip2
mapreduce.output.fileoutputformat.compress.type RECORD reducer 输出 SequenceFile 输出使用的压缩类型:NONE 和 BLOCK

1.3 开启 Map 输出阶段压缩

开启 map 输出阶段压缩可以减少 job 中 map 和 Reduce task 间数据传输量。具体配置如下:

案例实操:

1.开启 hive 中间传输数据压缩功能

hive(default)>set hive.exec.compress.intermediate=true;

2.开启 mapreduce 中 map 输出压缩功能

hive (default)>set mapreduce.map.output.compress=true;

3.设置 mapreduce 中 map 输出数据的压缩方式

hive (default)>set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;

4.执行查询语句

hive (default)> select count(ename) name from emp;

1.4 开启 Reduce 输出阶段压缩

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

1.开启 hive 最终输出数据压缩功能

hive (default)>set hive.exec.compress.output=true;

2.开启 mapreduce 最终输出数据压缩

hive (default)>set mapreduce.output.fileoutputformat.compress=true;

3.设置 mapreduce 最终数据输出压缩方式

hive (default)> set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;

4.设置 mapreduce 最终数据输出压缩为块压缩

hive (default)> set mapreduce.output.fileoutputformat.compress.type=BLOCK;

5.测试一下输出结果是否是压缩文件

hive (default)> insert overwrite local directory 
			  > '/opt/module/data/snappy-result' 
			  > select * from emp distribute by deptno sort by empno desc;
  1. 查看结果
[root@hadoop102 snappy-result]# cd /opt/module/data/snappy-result/

[root@hadoop102 snappy-result]# ll
总用量 4
-rw-r--r--. 1 root root 446 4月  12 16:54 000000_2.snappy

[root@hadoop102 snappy-result]# cat 000000_2.snappy 
¶󎵹34MILLERCLERK77821982-1-231300.0\N10
7902FORDANALYST75661981-12-330.20.0JAMES698[1	950	Z,30
1-175000,787-51.YL844TURNERSALESMAN]9-8150a^L39KINGPRESIDENT\N
|SCOTTB断-4-196柲8CLARKMANAGER7q%6-924耝!BLAKEJ.5-128.º$654MARTINV镋812114A5!JONESJc4-22975){!§521WARDNa
-1$369SMITHMgAK2a5`499ALLER2-2016
               1980A
                    817800.0\N20

可以看到结果已经是采用了 snappy 的方式压缩过了的、

二、 存储

2.1 行存储和列存储

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

2.1.1 行存储的特点

查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方, 所以此时行存储查询的速度更快。

TEXTFILE 和 SEQUENCEFILE 的存储格式都是基于行存储的;

2.1.2 列存储的特点

因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。

ORC 和 PARQUET 是基于列式存储的。

2.2 TextFile 格式

默认格式, 数据不做压缩, 磁盘开销大、 数据解析开销大。 可结合 Gzip、 Bzip2 使用, 但使用 Gzip 这种功能方式, Hive 不会对数据进行切分, 从而无法对数据进行并行操作。

2.3 Orc 格式

Orc (Optimized Row Columnar)是 Hive 0.11 版里引入的新的存储格式。

所示可以看到每个 Orc 文件由 1 个或多个 stripe 组成,每个 stripe250MB 大小, 这个 Stripe 实际相当于 RowGroup 概念,不过大小由 4MB ~ 250MB, 这样应该能提升顺序读的吞吐率。每个 Stripe 里有三部分组成,分别是 Index Data, Row Data, Stripe Footer:

1) Index Data:一个轻量级的 index,【默认是每隔 1W 行】做一个索引。这里做的索引应该只是记录某行的各字段在 Row Data 中的 offset。

2) Row Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个 Stream 来存储。

3) Stripe Footer:存的是各个 Stream 的类型,长度等信息。

每个文件有一个 File Footer,这里面存的是每个 Stripe 的行数,每个 Column 的数据类型信息等;每个文件的尾部是一个 PostScript,这里面记录了整个文件的压缩类型以及 FileFooter 的长度信息等。在读取文件时,会 seek 到文件尾部读 PostScript,从里面解析到 File Footer 长度,再读 FileFooter,从里面解析到各个 Stripe 信息,再读各个 Stripe,即【从后往前读】。

严格来说, Orc 应该叫做【行列式存储】。

2.4 Parquet 格式

Parquet 是面向分析型业务的列式存储格式,由 Twitter 和 Cloudera 合作开发, 2015 年 5 月从 Apache 的孵化器里毕业成为 Apache 顶级项目。

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

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

Parquet 的一个文件中可以存储多个行组,文件的首位都是该文件的 Magic Code,用于校验它是否是一个 Parquet 文件, Footer length 记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的 Schema 信息。除了文件中每一个行组的元数据,每一页的开始都会存储该页的元数据,在 Parquet 中,有三种类型的页: 数据页、字典页和索引页。数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引,目前 Parquet 中还不支持索引页。

严格来说, Parquet 也应该叫做【行列式存储】。

2.5 主流文件存储格式对比实验

从存储文件的【压缩比】和【查询速度】两个角度对比。

2.5.1 存储文件的压缩比测试

  1. TextFile

创建表并导入数据:

hive (default)> create table log_text (
              > track_time string,
              > url string,
              > session_id string,
              > referer string,
              > ip string,
              > end_user_id string,
              > city_id string
              > )
              > row format delimited fields terminated by '\t'
              > stored as textfile;
OK
Time taken: 0.567 seconds

hive (default)> load data local inpath 
              > '/opt/module/data/log.data'
              > into table log_text;
Loading data to table default.log_text
Table default.log_text stats: [numFiles=1, totalSize=19014996]
OK
Time taken: 0.923 seconds

查看大小:

hive (default)> dfs -du -h /user/hive/warehouse/log_text;
18.1 M  /user/hive/warehouse/log_text/log.data
  1. Orc:

创建表并导入数据:

hive (default)> create table log_orc(
              > track_time string,
              > url string,
              > session_id string,
              > referer string,
              > ip string,
              > end_user_id string,
              > city_id string
              > )
              > row format delimited fields terminated by '\t'
              > stored as orc ;
OK
Time taken: 0.156 seconds

hive (default)> insert into log_orc
			  > select * from log_text;

... ...
OK
log_text.track_time	log_text.url	log_text.session_id	log_text.referer	log_text.ip	log_text.end_user_id	log_text.city_id
Time taken: 19.947 seconds

【注】此处就不能使用 load data local inpath 的命令添加数据了, 得使用 insert into table 的命令从 log_text 中查出来数据, 放到 log_orc 表里面; 使用了 MR 的方式, 才会存成 Orc 格式;

查看大小:

hive (default)> dfs -du -h /user/hive/warehouse/log_orc;
2.8 M  /user/hive/warehouse/log_orc/000000_0
  1. Parquet:

创建表并导入数据:

hive (default)> create table log_parquet(
              > track_time string,
              > url string,
              > session_id string,
              > referer string,
              > ip string,
              > end_user_id string,
              > city_id string
              > )
              > row format delimited fields terminated by '\t'
              > stored as parquet ;
OK
Time taken: 0.106 seconds

hive (default)> insert into table log_parquet
              > select * from log_text;

... ...
OK
log_text.track_time	log_text.url	log_text.session_id	log_text.referer	log_text.ip	log_text.end_user_id	log_text.city_id
Time taken: 20.882 seconds

【注】同样使用 insert into 的形式通过 MR 导入数据, 使之存成 Parquet 格式;

查看大小:

hive (default)> dfs -du -h /user/hive/warehouse/log_parquet;
13.1 M  /user/hive/warehouse/log_parquet/000000_0
  1. 总结

存储文件的压缩比总结:

ORC > Parquet > textFile

2.5.2 存储文件的查询速度测试

  1. TestFile
hive (default)> select count(*) from log_text;

... ...
OK
_c0
100000
Time taken: 16.086 seconds, Fetched: 1 row(s)
  1. Orc:
hive (default)> select count(*) from log_orc;

... ...
OK
_c0
100000
Time taken: 15.88 seconds, Fetched: 1 row(s)
  1. Parquet:
hive (default)> select count(*) from log_parquet;

... ...
OK
_c0
100000
Time taken: 17.375 seconds, Fetched: 1 row(s)
  1. 总结

查询速度基本相同, 孤例不为证, 多次试验基本也相仿;

2.6 总结

基于数据文件的存储压缩比和查询速度的比较, 企业中 Orc 的存储方式比较受欢迎。

三、 测试压缩和存储

3.1 创建一个非压缩的 Orc 存储方式

创建表并导入数据:

hive (default)> create table log_orc_none(
              > track_time string,
              > url string,
              > session_id string,
              > referer string,
              > ip string,
              > end_user_id string,
              > city_id string
              > )
              > row format delimited fields terminated by '\t'
              > stored as orc tblproperties ("orc.compress"="NONE");
OK
Time taken: 0.329 seconds

hive (default)> insert into table log_orc_none select * from
              > log_text ;

... ...
OK
log_text.track_time	log_text.url	log_text.session_id	log_text.referer	log_text.ip	log_text.end_user_id	log_text.city_id
Time taken: 13.373 seconds

查看大小:

hive (default)> dfs -du -h /user/hive/warehouse/log_orc_none;
7.7 M  /user/hive/warehouse/log_orc_none/000000_0

3.2 创建一个 Snappy 压缩的 Orc 存储方式

创建表并导入数据:

hive (default)> create table log_orc_snappy(
              > track_time string,
              > url string,
              > session_id string,
              > referer string,
              > ip string,
              > end_user_id string,
              > city_id string
              > )
              > row format delimited fields terminated by '\t'
              > stored as orc tblproperties ("orc.compress"="SNAPPY");
OK
Time taken: 0.103 seconds

hive (default)> insert into table log_orc_snappy select * from
              > log_text ;

... ...
OK
log_text.track_time	log_text.url	log_text.session_id	log_text.referer	log_text.ip	log_text.end_user_id	log_text.city_id
Time taken: 12.056 seconds

查看大小:

hive (default)> dfs -du -h /user/hive/warehouse/log_orc_snappy;
3.8 M  /user/hive/warehouse/log_orc_snappy/000000_0

3.3 小总结

在上一节中的 “主流文件存储格式对比实验” 中进行的测试时, 压缩后的 Orc 的表的文件只有 2.8 M, 而即使使用 Snappy 压缩后的数据依然有 3.8 M, 原因是 Orc 存储文件默认采用的是 Zlib 压缩(Orc 自带的), 比 Snappy 压缩的还小。

四、 存储方式和压缩总结

在实际的项目开发当中, hive 表的数据存储格式一般选择: orc 或 parquet。压缩方式一般选择 snappy、 lzo。

【注】如果是最终的结果表, 可以使用 Orc 的存储格式, 因为占用的空间小; 如果是中间表, 后续还需要使用它进行计算, 就不能使用 Orc, 因为 Orc 是列式存储, 而 Hadoop 读取数据是按行读取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章