开启Map端输出阶段压缩
当Hive使用MR引擎时,也可以使用MR的相关压缩。
开启Map输出阶段压缩可以减少 jbo 中 map 和Reduce Task 间数据传输量。
步骤:
- 开启hive中间传输数据压缩功能
set hive.exec.compress.intermediate=true;
- 开启 mapreduce 中map输出压缩功能
set mapreduce.map.output.compress=true;
- 设置 mapreduce 中map输出数据的压缩方式
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
- 执行查询语句
select count(ename) name from emp;
开启Reduce输出阶段压缩
当Hive将输出写入到表中时,输出内容同样可以进行压缩。属性 hive.exec.compress.output
控制这个功能。用户可能需要保持默认设置文件中的默认值false
,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true
来开启输出结果压缩功能。
步骤:
- 开启Hive最终输出数据压缩功能
set hive.exec.compress.output=true;
- 开启MapReduce最终输出数据压缩
set mapreduce.output.fileoutputformat.compress=true;
- 设置MapReduce最终数据输出压缩方式
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
- 设置MapReduce最终输出压缩为块压缩(默认行压缩)
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
测试输出结果是否为压缩文件:
insert overwrite local directory '/home/tengyer/data/distribute-result'
select * from emp distribute by deptno sort by empno desc;
文件存储格式
Hive支持的存储数据的格式有:TEXTFILE、SEQUENCEFILE、ORC、PARQUET。
行存储和列存储
行存储和列存储示意图:
对于同样的一张表,可以有行存储和列存储两种策略。Row layout
为行存储,Column layout
为列存储。
当要查询某一行数据的时候(例如select * from emp;
),行存储查询的速度比列存储更快;
当要按字段进行查询时候(例如select sal from emp;
),列存储的速度比行存储更快。
而且因为每个字段的数据类型一定是相同的,所以列式存储可以针对性的设计更好的压缩算法。
在大数据应用中,一般都是对列进行统计分析,所以一般使用列存储的方式。
TextFile、SequenceFile的存储格式是基于行存储的;
Orc 和 Parquet 是基于列式的。
TextFile格式
默认的格式,数据不做压缩,磁盘开销大,数据解析开销大。
可以结合 Gzip、Bzip2 使用。但是使用 Gzip 时,Hive不会对数据进行切分,从而无法对数据进行并行操作。
创建表时指定数据的存储格式为textfile:
create table test(
id string,
name string
)
row format delimited fields terminated by ','
-- 指定存储格式为Textfile(默认就是Textfile)
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 Data
,Row Data
,Stripe Footer
。
示意图:
其中:
Index Data
:一个轻量级Index,默认是每隔1万行做一个索引。这里做的索引只是记录某行的个字段在Row Data
中的offsetRow Data
:存的是具体数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个Stream
来存储Stripe Footer
:存的是各个Stream
的类型、长度等信息
每个文件有一个 File Footer
,连存的是每个Stripe
的行数,每个Column
的数据类型信息;每个文件的尾部是一个PostScript
,里面记录了整个文件的压缩类型以及File Footer
的长度信息等。
在读取文件时,会seek
到文件尾部读取PostScript
,从里面解析到File Footer
长度,再读File Footer
,从里面解析到各个Stripe
信息,再读各个Stripe
。即从后往前读。
创建表时指定数据的存储格式为orc:
create table test(
id string,
name string
)
row format delimited fields terminated by ','
-- 指定存储格式为orc
stored as orc
-- 设置orc不使用压缩(orc默认使用了压缩,可以指定为不使用压缩)
tblproperties("orc.compress"="NONE");
Parquet格式
Parquet
文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此 Parquet
格式文件是自解析的。
Row Group
:行组。每一个行组包括一定的行数,在一个 hdfs 文件中至少存储一个行组,类似 ORC 的stripe
概念Column Chunk
:列块。在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩Page
:页。每个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。
通常情况下,在存储 Parquet
数据的时候会按照block
大小设置行组的大小,由于一般情况下每一个Mapper
任务处理数据的最小单位是一个Block
,这样可以把每一个行组由一个Mapper
任务处理,增大任务执行并行度。
示意图:
创建表时指定数据的存储格式为parquet:
create table test(
id string,
name string
)
row format delimited fields terminated by ','
-- 指定存储格式为parquet
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
压缩:
create table test(
id string,
name string
)
row format delimited fields terminated by ','
-- 指定存储格式为orc
stored as orc
-- 设置orc使用ZLIB压缩,也可以使用SNAPPY等其他压缩算法
tblproperties("orc.compress"="ZLIB");
Parquet使用SNAPPY压缩示例:
create table test(
id string,
name string
)
row format delimited fields terminated by ','
-- 指定存储格式为parquet
stored as parquet
-- 设置parquet使用SNAPPY压缩
tblproperties("parquet.compression"="SANPPY");