数据开发之离线计算_Hive存储格式与压缩格式&MapReduce输出Text文件压缩格式优缺点对比

数据开发之离线计算_Hive存储格式与压缩格式&MapReduce输出Text文件压缩格式优缺点对比

1.Hive存储格式

hive底层数据是存储在hdfs系统中的文件,hive的存储格式和压缩格式是两个不同的概念。存储格式是指数据按照何种规则存储在hdfs文件中,如TextFile(行式存储)、SequenceFile(行式存储)、ORC(列式存储)、Parquet(列式存储)等;压缩格式是指hdfs文件以何种规则进行压缩,如Gzip、Lzo、Bzip2、Default、Snappy、Zstd等。

1.1 TextFile

TextFile是hive默认的文件存储格式,存储方式为按行存储。

1.2 SequenceFile

SequenceFile是一种二进制存储文件,存储方式为按行存储,将每行数据以key-value的形式进行序列化后保存到文件中。

SequenceFile支持三种压缩格式:NONE、RECORD、BLOCK,且压缩后的SequenceFile也支持切片。

1.3 ORC

ORC是hive特有的数据存储格式,存储方式按列存储。具体操作就是将数据按行进行分块,每个分块的大小默认就是hdfs的block大小,也就是ORC文件的每个分块默认就保存在hdfs的一个block上,然后每个分块内的数据按列存储,每个分块内都由列索引、列数据、文件目录信息三部分组成。

未压缩的ORC文件支持切片。

1.4 Parquet

Parquet是一种二进制存储文件,存储方式为按列存储。

2.Hive表常用存储格式与压缩格式搭配

2.1 Hive建表语句设置存储格式

Hive表数据存储在文件系统上,必须要有文件存储格式来规范化数据的存储,按照约定好的格式来写数据和读数据。Hive有一些已经定义好的存储格式,也支持用户自定义文件存储格式,我们一般在建表时通过file_format和row_format两部分来设置该表的存储格式。

先不聊自定义文件存储格式,主要关注如何使用Hive已经定义好的存储格式。

Hive建表语句中常见的file_format和row_format设置格式如下:

1
2
[ROW FORMAT row_format] 
[STORED AS file_format]

其中row_format用于定义行数据存储格式,可以选择的设置方式有两种:

1
2
3
1.DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char] [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char] [NULL DEFINED AS char]

2.SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]

其中file_format用于定义文件数据存储格式,可以选择的设置方式有七种:

1
2
3
4
5
6
7
1.SEQUENCEFILE
2.TEXTFILE
3.RCFILE
4.ORC
5.PARQUET
6.AVRO
7.INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname

其实说到底通过这些file_format和row_format的设置主要需要选择SERDE、INPUTFORMAT、OUTPUTFORMAT三个参数选用哪些类。他们之间的关系如官方文档所说,SerDe就是行数据的序列化与反序列化规则。

1
2
3
4
SerDe is a short name for “Serializer and Deserializer.”
Hive uses SerDe to read and write table rows.
HDFS files –> InputFileFormat –> <key, value> –> Deserializer –> Row object
Row object –> Serializer –> <key, value> –> OutputFileFormat –> HDFS files

实际上STORED AS后面可以选择的file_format中有一部分已经将SERDE、INPUTFORMAT、OUTPUTFORMAT三者都已经设定好了,而另一部分只设定了INPUTFORMAT、OUTPUTFORMAT两个参数。

2.2 TextFile不压缩

STORED AS TEXTFILE只设定了INPUTFORMAT、OUTPUTFORMAT两个参数,等效于:

1
2
3
4
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat'

当使用STORED AS TEXTFILE时,如果ROW FORMAT中也没有设置SERDE参数,则默认使用’org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe’来作为SERDE参数。

2.3 TextFile+Lzo

我们公司最常见的要推到hbase中的app表存储格式和压缩格式就是TextFile+Lzo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE EXTERNAL TABLE if not exists app.app_zh_industry_market_trend_off_line_compare_scndcatg( 
terminal_type int COMMENT '渠道',
deal_sale_amount_index double COMMENT '成交金额指数')
COMMENT '行业趋势-大盘走势'
PARTITIONED BY (
dt string COMMENT '时间戳'
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES (
'field.delim'='\t'
)
STORED AS INPUTFORMAT "com.hadoop.mapred.DeprecatedLzoTextInputFormat"
OUTPUTFORMAT "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"
LOCATION 'hdfs://ns3/user/mart_sz/bi_compass/app.db/app_zh_industry_market_trend_off_line_compare_scndcatg';

查看存储路径可以看到问出文件名为hdfs://ns3/user/mart_sz/bi_compass/app.db/app_zh_industry_market_trend_off_line_compare_scndcatg/dt=2022-08-24/000000_0.lzo,压缩文件后缀名为lzo。

2.4 ORC不压缩

我们公司最常见的gdm、adm表存储格式和压缩格式就是ORC不压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE EXTERNAL TABLE if not exists adm.adm_zh_industry_market_trend_offline_scndcatg( 
terminal_type string COMMENT '渠道',
deal_sale_amount double COMMENT '成交金额')
COMMENT '行业趋势-大盘走势'
PARTITIONED BY (
dt string COMMENT '时间戳'
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.orc.OrcSerde'
WITH SERDEPROPERTIES (
'field.delim'='\t'
) STORED AS orc
LOCATION
'hdfs://ns3/user/mart_sz/bi_compass/adm.db/adm_zh_industry_market_trend_offline_scndcatg';

STORED AS ORC直接设定了SERDE、INPUTFORMAT、OUTPUTFORMAT三个参数,等效于:

1
2
3
4
5
6
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.orc.OrcSerde'
STORED AS INPUTFORMAT
'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'

查看存储路径可以看到存储文件名为hdfs://ns3/user/mart_sz/bi_compass/adm.db/adm_zh_industry_market_trend_offline_scndcatg/dt=2022-08-07/part-00000-3a1cfbf7-f62d-4df7-89c4-74beacd628f3,没有压缩格式后缀名。

3.MapReduce输出Text文件压缩格式

3.1 MapReduce压缩格式选择

MapReduce计算任务输入时不需要指定压缩算法,计算任务会自动检查文件的后缀名,然后选择对应的压缩算法对输入文件进行解压。

MapReduce计算任务设置压缩算法主要是对map输出和reduce输出而言的:为mapper输出设置压缩格式可以减少中间临时存储所占用的空间,减少网络io;为reduce输出设置压缩格式可以减少最终输出存储所占用的空间。但是为输出设置压缩算法会使计算时间变长,因为解压缩比较耗时。

还有需要重点考虑的是最终输出文件所采用的压缩方式是否支持分片,如果不支持分片则该文件被下一个MapReduce任务处理是,只能使用一个map来处理一整个文件。常见的Text压缩算法有Gzip、Lzo、Bzip2、Default、Snappy、Zstd,其中只有Bzip2是支持分片的,但是Bzip2的解压缩耗时又是最长的。

配置map输出压缩算法代码
1
2
3
4
//开启map端输出压缩
conf.setBoolean("mapreduce.map.output.compress", true);
//设置map端输出压缩方式,压缩算法可以选择GzipCodec.class、BZip2Codec.class、ZStandardCodec.class等。
conf.setClass("mapreduce.map.output.compress.codec", BZip2Codec.class, CompressionCodec.class);
配置reduce输出压缩算法代码
1
2
3
4
//开启reduce端输出压缩
FileOutputFormat.setCompressOutput(job, true);
//设置reduce端输出压缩方式,压缩算法可以选择GzipCodec.class、BZip2Codec.class、ZStandardCodec.class等。
FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);

3.2 压缩格式对比

上文中讲到我按照十六进制字符串格式解码hbase快照数据之后存储到Text文件中,如果不对这个Text文件进行压缩,那么所需要的存储空间会是hfile文件的十几倍,我选择使用压缩算法以快照读取时间换数据存储空间。

现根据日志对不压缩、Gzip、BZip2、ZStandard四种方式从压缩率、计算时间、可否分片三个角度进行对比。

Text文件数据重推回hbase过程中设置输入分片大小为100M:

1
2
3
//重要:Text文件输入分片大小
Long minsize = 100000000L;
conf.setLong("mapreduce.input.fileinputformat.split.minsize", minsize);
不压缩

快照读取日志:

1
2
3
4
5
6
7
[2022-08-01T17:10:14.445+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_1698579101801_1199082 completed successfully
[2022-08-01T17:10:14.577+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 48
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=27656259760
[2022-08-01T17:10:14.578+08:00] [INFO] ads.hbase2hive.GetHbaseTool.run(GetHbaseTool.java 160) [main] : spent time:910

重推到hbase日志:

1
2
3
4
5
6
[2022-08-01T17:53:09.388+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_8091494100801_19551256 completed successfully
[2022-08-01T17:53:09.752+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 53
Killed map tasks=6
Launched map tasks=86
Launched reduce tasks=10
Other local map tasks=86
Gzip

快照读取日志:

1
2
3
4
5
6
7
[2022-08-01T17:12:48.403+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_8091494100801_19543019 completed successfully
[2022-08-01T17:12:48.501+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 48
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=1143284839
[2022-08-01T17:12:48.502+08:00] [INFO] ads.hbase2hive.GetHbaseTool.run(GetHbaseTool.java 160) [main] : spent time:1015

重推到hbase日志:

1
2
3
4
5
6
[2022-08-01T18:28:00.834+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_8091494100801_19555491 completed successfully
[2022-08-01T18:28:00.933+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 53
Killed map tasks=4
Launched map tasks=59
Launched reduce tasks=10
Other local map tasks=59
BZip2

快照读取日志:

1
2
3
4
5
6
7
[2022-08-01T17:34:23.111+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_8091494100801_19543036 completed successfully
[2022-08-01T17:34:23.193+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 48
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=739276228
[2022-08-01T17:34:23.194+08:00] [INFO] ads.hbase2hive.GetHbaseTool.run(GetHbaseTool.java 160) [main] : spent time:2300

重推到hbase日志:

1
2
3
4
5
6
[2022-08-01T18:26:08.242+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_1698579101801_1210959 completed successfully
[2022-08-01T18:26:08.332+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 53
Killed map tasks=13
Launched map tasks=72
Launched reduce tasks=10
Other local map tasks=72
ZStandard

快照读取日志:

1
2
3
4
5
6
7
[2022-08-01T17:14:54.008+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_8091494100801_19543124 completed successfully
[2022-08-01T17:14:54.097+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 48
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=1198029197
[2022-08-01T17:14:54.098+08:00] [INFO] ads.hbase2hive.GetHbaseTool.run(GetHbaseTool.java 160) [main] : spent time:1090

重推到hbase日志:

1
2
3
4
5
6
[2022-08-01T18:28:37.733+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1417) [main] : Job job_8091494100801_19555530 completed successfully
[2022-08-01T18:28:37.815+08:00] [INFO] hadoop.mapreduce.Job.monitorAndPrintJob(Job.java 1424) [main] : Counters: 53
Killed map tasks=4
Launched map tasks=59
Launched reduce tasks=10
Other local map tasks=59

总结上述日志可知,未设置压缩格式的Text文件大小是27.6GB,Gzip格式的是1.1GB,BZip2格式的是0.7GB,ZStandard格式的是1.2GB;

未设置压缩格式的Text文件读取与存储时间910s,Gzip格式的是1015s,BZip2格式的是2300s,ZStandard格式的是1090s;

读取Gzip压缩格式Text文件的map数是55,BZip2格式的是59,ZStandard格式的是55,说明Gzip、ZStandard不可分片,BZip2可分片。

参考文献

HIVE STORED&Row format