NightPxy 个人技术博客

Hadoop 压缩

Posted on By NightPxy

使用压缩的优缺点

优点

  • 减少存储空间
  • 减少网络传输

缺点

  • 消耗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的劣势是切割必须依赖索引,但这是可以克服的,没有索引可以创建索引等等

  • LZ4snappy
    这两者非常类似,都属于快速压缩,即拥有最快的压缩速度和可以接收的压缩比,都有共同的缺点是不支持分割
    这两者都适合特别追求压缩速度,或者目标文件本身就不大的场景
    这两者之间 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