使用压缩的优缺点
优点
- 减少存储空间
- 减少网络传输
缺点
- 消耗CPU的计算能力
压缩方式的对比
压缩方式的对比主要从 压缩特性 和 压缩性能 两个维度进行比较
- 压缩特性
压缩特性主要指是否内置,是否native等,但最重要的核心指标是 是否可分割
压缩是否可分割,代表着是否可以并行读取每个文件块
举个例子说:
一个可分割的N块压缩文件,将会启动N个Map来并行读取 一个不可分割的N块压缩文件,因为无法随机读取,所以只会以一个Map来读取整个文件,这是非常有影响的
所以,大文件尽可能选择可切分特性,如果使用不可切分,尽可能使用文件不足一块的小文件中 - 压缩性能
压缩性能的核心指标是 压缩比 和 压缩/解压速率
这两者往往是反比关系,即更好的压缩比,往往是以更慢的压缩/解压速度为代价
各压缩方式的详细指标对比如下:
压缩特性
格式 | codec | 后缀 | 切割 | native | 工具 | 内置 |
---|---|---|---|---|---|---|
gzip | org.apache.hadoop.io.compress.GzipCodec | .gz | 否 | 是 | gzip | 是 |
bzip2 | org.apache.hadoop.io.compress.BZip2Codec | .bz2 | 是 | 否 | bzip2 | 是 |
lzo | com.hadoop.compression.lzo.LzopCodec | .lzo | 条件是 | 是 | lzop | 否 |
snappy | org.apache.hadoop.io.compress.SnappyCodec | .snappy | 否 | 是 | 无 | 否 |
LZ4 | org.apache.hadoop.io.compress.Lz4Codec | .lz4 | 否 | 是 | 无 | 否 |
*压缩性能
压缩格式 | 压缩比 | 压缩速率 | 解压速率 |
---|---|---|---|
gzip | 13.4% | 21 MB/s | 118 MB/s |
lzo | 20.5% | 135 MB/s | 410 MB/s |
snappy | 22.2% | 172 MB/s | 409 MB/s |
bzip2 | 13.2% | 2.4MB/s | 9.5MB/s |
详细分析
-
gzip
gzip 有非常好的压缩比,但是压缩速度比较差,并且不支持切割 所以gzip用处是比较小的,因为各方面的特点都不明显.追求压缩比的场景下,唯一比bzip2好的就是稍块的速度,但是会失去bzip2天然的可分割特性 -
lzo
如果要使用大文件的MapReduce计算,lzo应该是首选
lzo 有不错的压缩比和非常不错的加解压速度,并且可以是可切割的
lzo的劣势是切割必须依赖索引,但这是可以克服的,没有索引可以创建索引等等 -
LZ4 与 snappy
这两者非常类似,都属于快速压缩,即拥有最快的压缩速度和可以接收的压缩比,都有共同的缺点是不支持分割
这两者都适合特别追求压缩速度,或者目标文件本身就不大的场景
这两者之间 LZ4是后期之秀,相比snappy压缩速度会更快一点 -
bzip2
具有最好的压缩比,并且天然支持随机读,但是有最慢的压缩速度(并且慢的不止一点). 与lzo这种相比,压缩比可以提升大约1倍,但是压缩时间需要多10倍
特定条件下使用,比如归档数据,只是单纯保存,很少再使用了
压缩格式的选择
-
与容器格式配合使用,特别是ORC或者Parquet,列式存储将相同列(列结构相同)存储在一起,可以非常大的提高压缩比
-
对于大型文件,尽可能使用可切割压缩,否则会造成MR执行效率非常低下 对于大型文件还有一个思路是在应用端先行切割,再对分块进行快速压缩.此时应用端切割时应考虑压缩之后的大小,力求将压缩之后的块接近HDFS块大小
-
对于小型文件(文件不超过一个HDFS块),可以考虑使用快速压缩
MapReduce 中的压缩
Map输入
如果文件是压缩的,那么会根据文件扩展名推断出相应的Codec,进行相应的解压读取
如果是可分块压缩方式,就会启动多个Map分块读取,如果是不可分块压缩,就会启动一个Map读取整个文件
Map输出
Map的输出会写到磁盘并通过网络传输到Reduce,这里是一个适用压缩的场景
这里适合Snappy或者LZ4之类的快速压缩
- Map输出是溢写块,一般情况下本身就不大,适合使用快速压缩.
- 可分割的压缩带来的随机读好处.reduce享受不了,因为最终一个分组只会有一个Reduce读取
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
mapred.output.compress | false | ||
mapred.compress.map.output | |||
mapred.output.compression.codec | |||
mapred.output.compression.type |
Reduce 输出
Reduce会将结果最终输出到HDFS,这里也是适用压缩场景
Reduce输出的压缩要考虑具体场景
- 如果是输出为小文件就可以考虑快速压缩
- 如果是大文件,且该输出是另一个任务的输入就建议使用lzo并创建索引
- 如果是不考虑时间和CPU消耗的归档数据,可以考虑用bzip2压缩
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
mapreduce.output.fileoutputformat.compress | false | ||
mapreduce.output.fileoutputformat.compress.codec | org.apache.hadoop.io.compress.DefaultCodec | ||
mapreduce.output.fileoutputformat.compress.type | RECORD |
补充
修改默认压缩
首先,应该保证Hadoop每个节点的压缩方式都是可用的,并且最好是native支持的
其次,需要在Hadoop集群配置中注册压缩方式
然后,通过配置在需要的地方启用压缩
检查Hadoop压缩 native支持
hadoop checknative
注册压缩方式 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,
org.apache.hadoop.io.compress.SnappyCodec,
com.hadoop.compression.lzo.LzopCodec,
org.apache.hadoop.io.compress.Lz4Codec
</value>
</property>
需要的地方启用压缩,例如Shuffle压缩 mapred-site.xml
<!-- 配置 Map段输出的压缩,snappy-->
<property>
<name>mapreduce.map.output.compress</name>
<value>true</value>
</property>
<property>
<name>mapreduce.map.output.compress.codec</name>
<value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>
LZO 创建索引
注意点:
- 创建目标索引的前提是不会再改变了,如果文件后续还会被追加写入,创建索引没有意义
- 只支持对TextFile进行压缩
- 索引文件默认在目标文件所处的文件夹
/hadoop jar hadoop-lzo.jar com.hadoop.compression.lzo.LzoIndexer hdfspath/xxx.lzo
LzoIndexer lzoIndexer = new LzoIndexer(conf);
lzoIndexer.index(new Path("hdfs-lz4-file-path"));
参考博客 http://www.hainiubl.com/topics/26