Hadoop 压缩介绍

Posted by JustDoDT on April 15, 2018

压缩简介

在大数据领域,压缩是无法避免的话题,比如,在电商系统中,用户的行为数据越来越大,当达到一定的量时,将会面临着,怎么快速地处理这些数据。 在Hadoop 生态系统中,对数据进行压缩处理使得提高我们的数据处理效率,如何选择压缩和使用压缩?

压缩

压缩是把数据“减少”的过程。

解压缩

将压缩过后的数据转换成原始数据的过程。

为什么使用压缩

  1. 减少文件大小
  2. 节省磁盘空间
  3. 增加网络传输的效率

常用的压缩技术

无损压缩(Lossless compression)

压缩和解压缩的过程中没有数据丢失

问题:在压缩的过程中,多余的数据去了哪里?

多余的数据必然是冗余的数据,借助一些算法,冗余的数据在压缩的过程中会被移除 在解压的过程中,借助同样的算法,再把它自动的补充上来 适用场景: 用户行为日志 如果是用来做计费的,数据必然是不能丢的,丢了公司就少盈利了

有损压缩(Lossy compression)

有损压缩(对于图片、视频的处理,可以采用有损压缩) 压缩过后和压缩之前的数据有损失 图片和视频丢掉几帧,凭肉眼是很难被区分出来的 好处: 技术成本低,压缩率 压缩比非常高 能够更节省空间,而且压缩的时间也非常短

压缩场景(以MapReduce为例)

从3个角度来看:输入,中间,输出

输入

如果输入文件是经过压缩的,那么数据压缩过后所占用的HDFS的空间就会少很多,通常情况下,也就意味着读取数据所耗费的时间也就比较少,但是,当CPU处于高负载的情况,所读取的时间也会很长。

MapReduce/Hive/Spark这些框架是可以读取压缩文件的,它们会自动进行解压 这些计算框架在读取文件之后,具备自动解压文件的功能 数据有没有压缩,分布式处理框架都是能自动识别的,并不需要去做额外的东西

涉及到一个问题:

不同的压缩方式它的codec是不一样的 比如:GzipCodec…

中间

以MapReduce来看,Map过程之后是有输出的 如果在做shuffle之前,对数据使用压缩,必然是会减少磁盘空间, 而在shuffle过程因为体积减少,传输效率也是会提高的 建议:中间过程也是采用压缩

输出

对于输出也是同理的,如果对于输出的文件是作为历史文件的,更应该使用压缩,而且需要选用压缩比非常高的压缩方式 这样历史数据,占用空间肯定少了;如果对于输出的文件继续做MapReduce则应该考虑是否支持分片,即split。

总结:

对于上述三点,不管哪一步,都建议采用压缩, 前提:CPU(如果CPU都不够用,就别采用压缩了)

压缩注意事项

从上述的来看,压缩能带来的好处有:

  • 节省存储空间、加速数据传输、减少磁盘与网络的IO
  • 这些对于大数据的执行性能是有改善的 但是压缩/解压缩的过程,CPU的利用率是很高的 在使用的时候,是针对集群的状况来做取舍的,如下图所示:

集群状况的压缩规则

因此:对于压缩的使用,是需要结合实际生产环境进行权衡的

压缩格式比较

压缩格式 压缩工具 压缩算法 文件扩展名 是否支持分片
deflate deflate .deflate
gzip gzip deflate .gz
lzo lzop lzo .lzo 是(需要index)
snappy snappy .snappy
bzip2 bzip2 bzip2 .bz2
lz4 lz4 .lz4

各种压缩性能测试

测试环境:
8 core i7 cpu
8GB memory
64 bit CentOS
1.4GB Wikipedia Corpus 2-gram text input (1.4G 文本类型的数据)

压缩比
压缩比

压缩时间 压缩时间

可以看出压缩比越高,压缩时间越长,压缩比:Snappy < LZ4 < LZO < GZIP < BZIP2

gzip

优点:

压缩比在四种压缩方式中较高;hadoop本身支持,在应用中处理gzip格式的文件就和直接处理文本一样;有hadoop native库;大部分linux系统都自带gzip命令,使用方便。

缺点:

不支持split。

lzo压缩

优点:

压缩/解压速度也比较快,合理的压缩率;支持split,是hadoop中最流行的压缩格式;支持hadoop native库;需要在linux系统下自行安装lzop命令,使用方便。

缺点:

压缩率比gzip要低;hadoop本身不支持,需要安装;lzo虽然支持split,但需要对lzo文件建索引,否则hadoop也是会把lzo文件看成一个普通文件(为了支持split需要建索引,需要指定inputformat为lzo格式)。

snappy压缩

优点:

压缩速度快;支持hadoop native库。

缺点:

不支持split;压缩比低;hadoop本身不支持,需要安装;linux系统下没有对应的命令。

bzip2压缩

优点:

支持split;具有很高的压缩率,比gzip压缩率都高;hadoop本身支持,但不支持native;在linux系统下自带bzip2命令,使用方便。

缺点:

压缩/解压速度慢;不支持native。

选择合适的压缩格式

根据实际的业务场景来说:

  • 老/冷数据(即平时用的不多的数据)

选择压缩比高(因为既然作为历史的东西,肯定是用的不多的)

  • Flume收集上来的数据

选择压缩速度快的 & 选择是否支持分割 如何选择压缩格式,是需要出于对时间/空间的权衡的,如下图所示:

时间/空间的权衡

是否支持split

对于Hadoop/Spark作业(Hadoop指生态,Hive也是一样): 这些作业通常情况下,有一个特点,IO密集型(因为数据读取对于磁盘IO、网络IO等各方面都是比较重的) 能否支持分割对于一个作业的运行效率有很大的影响 假设数据大小为1G ==> 如果不支持分割,只能由1个mapper去处理 不支持分割的话,不管你的速度多快,肯定也会拖慢整个作业的执行时间,进而影响作业执行效率

怎么查看是否支持各种压缩

[hadoop@hadoop001 ~]$ cd $HADOOP_PREFIX
[hadoop@hadoop001 hadoop-2.6.0-cdh5.7.0]$ cd bin/
[hadoop@hadoop001 bin]$ ./hadoop checknative
19/03/31 05:58:12 INFO bzip2.Bzip2Factory: Successfully loaded & initialized native-bzip2 library system-native
19/03/31 05:58:12 INFO zlib.ZlibFactory: Successfully loaded & initialized native-zlib library
Native library checking:
hadoop:  true /home/hadoop/app/hadoop-2.6.0-cdh5.7.0/lib/native/libhadoop.so.1.0.0
zlib:    true /lib64/libz.so.1
snappy:  true /usr/lib64/libsnappy.so.1
lz4:     true revision:99
bzip2:   true /lib64/libbz2.so.1
openssl: true /usr/lib64/libcrypto.so
[hadoop@hadoop001 bin]$ 

怎么通过源码编译支持snappy 参考博客:

hadoop支持各种压缩的源码编译安装

hadoop-lzo压缩及测试

关于压缩格式是否支持分割的解读

When considering how to compress data that will be processed by MapReduce,it is important to understand whether the compression format supports splitting;Splittability must be taken into account;

If a compression method is splittable,every compressed input split can be extracted and processed independently.

常用的codec

压缩格式 压缩类
Zlib 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.LzoCodec
Lz4 org.apache.hadoop.io.compress.Lz4Codec
Snappy org.apache.hadoop.io.compress.SnappyCodec

压缩在MapReduce中的应用

压缩在MR中的应用

如上图所示,对于每个环节的技术选型如下:

Use Compressed Map Input(从输入层面考虑) Mapreduce jobs read input from HDFS MapReduce job从HDFS读数据 Compress if input data is large.This will reduce disk read cost 如果输入数据大的话采用压缩,这样将会减少磁盘数据的读取 Compress with splittable algorithms like Bzip2 压缩得考虑split是否支持 Or use compression with splittable file structures such as Sequence Files, RC File etc 使用压缩支持分割的文件的结构,如Sequence File、RC File

总结:使用压缩,必然要支持split

Compress Intermediate Data(从中间处理来考虑) Map output is written to disk(spill) and transferred accross the network Map的输出,会写到磁盘上面去,然后通过网络进行传输

Always use compression to reduce both disk write,and network transfer load 通常会采用压缩去减少磁盘的写、网络传输的负载

Beneficial in performace point if view even if input and output is uncompressed 性能上是有帮助的,即使输入和输出没有使用压缩,在中间使用压缩,在性能上也是有提升的

Use faster codecs such as Snappy,LZO 在这个阶段,使用速度更快的压缩方式,比如Snappy、LZO

Compress Reducer Output(从输出层面来考虑) Mapreduce output used for both archiving or chaining mapreduce jobs

MapReduce的输出有两种场景:

  • 归档(当作历史)

  • 作为其它MR作业的输入(两个MR配合起来使用)

Use compression to reduce disk space for archiving 使用压缩在归档的时候减少磁盘空间

Compression is also beneficial for chaining jobs especially with limited disk through put resource 在chain(操作链的情况下) 压缩通常情况在这种场景下是有用的

Use compression methods with higher compress ratio to save more disk space 在这个阶段得使用的压缩方法是高的压缩比去节省更多的节省空间 比如BZIP2(压缩比最高)

注意:如果作业是作为另外一个作业的输入的话,是需要考虑压缩方式是否支持split的

压缩在MapReduce中的应用

在hadoop中的core-site.xml 文件中添加如下内容

<property>
<name>io.compression.codecs</name>
	<value>
		org.apache.hadoop.io.compress.GzipCodec,
		org.apache.hadoop.io.compress.DefaultCodec,
		org.apache.hadoop.io.compress.BZip2Codec
	</value>
</property>

在mapred-site.xml 文件中添加如下内容

<!--启用map中间压缩类-->
<property>
   <name>mapred.map.output.compression.codec</name>
   <value>com.hadoop.compression.lzo.LzopCodec</value>
</property>
<!--启用mapreduce文件压缩-->
<property>
    <name>mapreduce.output.fileoutputformat.compress</name>
    <value>true</value>
</property> 
<!--启用mapreduce压缩类-->
<property>
   <name>mapreduce.output.fileoutputformat.compress.codec</name>
   <value>com.hadoop.compression.lzo.LzopCodec</value>
</property>
<!--配置Jar包-->
<property>
    <name>mapred.child.env</name>
    <value>LD_LIBRARY_PATH=/usr/local/lib</value>
</property>

测试

[hadoop@hadoop001 data]$ du -sh -m  data.log
1674	data.log


[hadoop@hadoop001 data]$ hdfs dfs -put data.log /hadoop/compress

[hadoop@hadoop001 lib]$ hadoop jar g6-hadoop-1.0_back.jar com.ruozedata.hadoop.mapreduce.driver.LogETLDriver             /hadoop/compress/data.log /hadoop/compress/output/bzip2
19/03/31 06:45:02 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
19/03/31 06:45:03 WARN mapreduce.JobResourceUploader: Hadoop command-line option parsing not performed. Implement the Tool interface and execute your application with ToolRunner to remedy this.
19/03/31 06:45:03 INFO input.FileInputFormat: Total input paths to process : 1
19/03/31 06:45:03 INFO lzo.GPLNativeCodeLoader: Loaded native gpl library from the embedded binaries
19/03/31 06:45:03 INFO lzo.LzoCodec: Successfully loaded & initialized native-lzo library [hadoop-lzo rev f1deea9a313f4017dd5323cb8bbb3732c1aaccc5]
19/03/31 06:45:04 INFO mapreduce.JobSubmitter: number of splits:13

hadoop-bzip2分片1

注意:number of splits:13

因为原始文件有1674MB,bzip默认是支持分片的,每个块的大小为128MB,1674MB/128MB=13.078 < 13.1 所以按照13 处理。

具体分片理论详细博客:MapReduce分片

[hadoop@hadoop001 lib]$ hdfs dfs -lsr /hadoop/compress/output/bzip2
lsr: DEPRECATED: Please use 'ls -R' instead.
-rw-r--r--   1 hadoop supergroup          0 2019-03-31 06:53 /hadoop/compress/output/bzip2/_SUCCESS
-rw-r--r--   1 hadoop supergroup  490067144 2019-03-31 06:53 /hadoop/compress/output/bzip2/part-r-00000.bz2
[hadoop@hadoop001 lib]$ 

hadoop-bzip2分片2

MapReduce中的Lzo压缩测试详见博客 MapReduce中的Lzo压缩测试

压缩在hive中的使用

hive (testdb)> CREATE TABLE compress_test(
             >             cdn string, 
             >             region string, 
             >             level string, 
             >             time string, 
             >             ip string, 
             >             domain string, 
             >             url string, 
             >             traffic bigint)
             >           ROW FORMAT DELIMITED 
             >             FIELDS TERMINATED BY '\t'   ;        
OK
Time taken: 0.044 seconds

导入数据

hive (testdb)> LOAD DATA LOCAL INPATH '/home/hadoop/data/data.log' OVERWRITE INTO TABLE compress_test;
Loading data to table testdb.compress_test
Table testdb.compress_test stats: [numFiles=1, numRows=0, totalSize=1754941024, rawDataSize=0]
OK
Time taken: 9.062 seconds
hive (testdb)> 

在HDFS上查看大小

[hadoop@hadoop001 data]$ hdfs dfs -du -h /user/hive/warehouse/testdb.db/compress_test 1.6 G 1.6 G /user/hive/warehouse/testdb.db/compress_test/data.log

用Bzip2测试

在Hive的当前会话配置,为了不影响其他程序运行

hive (testdb)>set hive.exec.compress.output=true;
hive (testdb)>set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.BZip2Codec;

创建compress_test_bzip2表

hive (testdb)>create table compress_test_bzip2 ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
AS SELECT * FROM compress_test;

Hive中的Bzip2测试

查看HDFS数据大小

Hive中的Bzip2测试

69.27 + 69.84 * 5 + 37.55 =456.02 MB < 1.6 GB

用Gzip测试

配置

hive (testdb)>set hive.exec.compress.output=true;
hive (testdb)>set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec;

创建compress_test_gzip表

hive (testdb)>create table compress_test_gzip ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
AS SELECT * FROM compress_test;

Hive中的Gzip测试

查看HDFS上的数据大小

Hive中的Gzip测试

84.02 + 84.79 * 3 + 84.8 * 2 + 45.59 = 553.58 MB < 1.6 GB

总结:

  • 经过上面对比,每种压缩都有各自的优势与劣势,我们在选择使用哪种压缩时,需要根据场景来判断,不同的场景选择不同的压缩方式

  • 例如:选择高压缩比,那么对于cpu的性能要求要高,同时压缩、解压时间耗费也多;

  • 选择压缩比低的,对于磁盘io、网络io的时间要多,空间占据要多;

  • 选择对于支持分割的,可以实现并行处理,不支持split的需要空置block的大小如果一个文件太大不支持split只有一个map去处理太耗时。