分享

HDFS支持数据压缩的几种方法探讨

goldtimes 发表于 2013-10-25 10:45:23 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 7 88006
本帖最后由 nettman 于 2014-2-18 16:22 编辑

HDFS支持数据压缩存在以下几种方法:

1、在HDFS之上将数据压缩好后,再存储到HDFS
2、在HDFS内部支持数据压缩,这里又可以分为几种方法:
    2.1、压缩工作在DataNode上完成,这里又分两种方法:
           2.1.1、数据接收完后,再压缩
                     这个方法对HDFS的改动最小,但效果最低,只需要在block文件close后,调用压缩工具,将block文件压缩一下,然后再打开block文件时解压一下即可,几行代码就可以搞定
           2.1.2、边接收数据边压缩,使用第三方提供的压缩库
                     效率和复杂度折中方法,Hook住系统的write和read操作,在数据写入磁盘之前,先压缩一下,但write和read对外的接口行为不变,比如:原始大小为100KB的数据,压缩后大小为10KB,当写入100KB后,仍对调用者返回100KB,而不是10KB
    2.2、压缩工作交给DFSClient做,DataNode只接收和存储
           这个方法效果最高,压缩分散地推给了HDFS客户端,但DataNode需要知道什么时候一个block块接收完成了。
推荐最终实现采用2.2这个方法,该方法需要修改的HDFS代码量也不大,但效果最高。

已有(4)人评论

跳转到指定楼层
tntzbzc 发表于 2015-6-1 11:37:50
本帖最后由 tntzbzc 于 2015-6-1 11:39 编辑

hadoop对于压缩文件的支持




hadoop对于压缩格式的是透明识别,我们的MapReduce任务的执行是透明的,hadoop能够自动为我们 将压缩的文件解压,而不用我们去关心。
如果我们压缩的文件有相应压缩格式的扩展名(比如lzo,gz,bzip2等),hadoop就会根据扩展名去选择解码器解压。
hadoop对每个压缩格式的支持,详细见下表:
压缩格式 工具 算法 文件扩展名 多文件 可分割性
DEFLATE DEFLATE .deflate
gzip gzip DEFLATE .gz
ZIP zip DEFLATE .zip 是,在文件范围内
bzip2 bzip2 bzip2 .bz2
LZO lzop LZO .lzo




如果压缩的文件没有扩展名,则需 要在执行mapreduce任务的时候指定输入格式.
hadoop jar /usr/home/hadoop/hadoop-0.20.2/contrib/streaming/hadoop-streaming-0.20.2-CD H3B4.jar -file /usr/home/hadoop/hello/mapper.py -mapper /usr/home/hadoop/hello/mapper.py -file /usr/home/hadoop/hello/reducer.py -reducer /usr/home/hadoop/hello/reducer.py -input lzotest -output result4 -jobconf mapred.reduce.tasks=1 *-inputformat org.apache.hadoop.mapred.LzoTextInputFormat*
hadoop下各种压缩算法的压缩比,压缩时间,解压时间见下表:
压缩算法原始文件大小压缩后的文件大小压缩速度解压缩速度
gzip  8.3GB  1.8GB17.5MB/s58MB/s
bzip28.3GB1.1GB2.4MB/s9.5MB/s
LZO-bset8.3GB2GB4MB/s60.6MB/s
LZO8.3GB2.9GB49.3MB/S74.6MB/s


  

hadoop各种压缩算法的优缺点简述
在考虑如何压缩那些将由MapReduce处理的数据时,考虑压缩格式是否支持分割是很重要的。考虑存储在HDFS中的未压缩的文件,其大小为1GB,HDFS的块大小为64MB,所以该文件将被存储为16块,将此文件用作输入的MapReduce作业会创建1个输人分片(split ,也称为“分块”。对于block,我们统一称为“块”。)每个分片都被作为一个独立map任务的输入单独进行处理。
现在假设。该文件是一个gzip格式的压缩文件,压缩后的大小为1GB。和前面一样,HDFS将此文件存储为16块。然而,针对每一块创建一个分块是没有用的,因为不可能从gzip数据流中的任意点开始读取,map任务也不可能独立于其他分块只读取一个分块中的数据。gzip格式使用DEFLATE来存储压缩过的数据,DEFLATE将数据作为一系列压缩过的块进行存储。问题是,每块的开始没有指定用户在数据流中任意点定位到下一个块的起始位置,而是其自身与数据流同步。因此,gzip不支持分割(块)机制。
在这种情况下,MapReduce不分割gzip格式的文件,因为它知道输入是gzip压缩格式的(通过文件扩展名得知),而gzip压缩机制不支持分割机制。这样是以牺牲本地化为代价:一个map任务将处理16个HDFS块。大都不是map的本地数据。与此同时,因为map任务少,所以作业分割的粒度不够细,从而导致运行时间变长。
在我们假设的例子中,如果是一个LZO格式的文件,我们会碰到同样的问题,因为基本压缩格式不为reader提供方法使其与流同步。但是,bzip2格式的压缩文件确实提供了块与块之间的同步标记(一个48位的PI近似值),因此它支持分割机制。
对于文件的收集,这些问题会稍有不同。ZIP是存档格式,因此它可以将多个文件合并为一个ZIP文件。每个文件单独压缩,所有文档的存储位置存储在ZIP文件的尾部。这个属性表明ZIP文件支持文件边界处分割,每个分片中包括ZIP压缩文件中的一个或多个文件

在MapReduce我们应该使用哪种压缩格式
根据应用的具体情况来决定应该使用哪种压缩格式。就个人而言,更趋向于使用最快的速度压缩,还是使用最优的空间压缩?一般来说,应该尝试不同的策略,并用具有代表性的数据集进行测试,从而找到最佳方法。对于那些大型的、没有边界的文件,如日志文件,有以下选项。
存储未压缩的文件。
使用支持分割机制的压缩格式,如bzip2。
在应用中将文件分割成几个大的数据块,然后使用任何一种支持的压缩格式单独压缩每个数据块(可不用考虑压缩格式是否支持分割)。在这里,需要选择数据块的大小使压缩后的数据块在大小上相当于HDFS的块。
使用支持压缩和分割的Sequence File(序列文件)。
对于大型文件,不要对整个文件使用不支持分割的压缩格式,因为这样会损失本地性优势,从而使降低MapReduce应用的性能。

hadoop支持Splittable压缩lzo
在hadoop中使用lzo的压缩算法可以减小数据的大小和数据的磁盘读写时间,在HDFS中存储压缩数据,可以使集群能保存更多的数据,延长集群的使用寿命。不仅如此,由于mapreduce作业通常瓶颈都在IO上,存储压缩数据就意味这更少的IO操作,job运行更加的高效。
但是在hadoop上使用压缩也有两个比较麻烦的地方:第一,有些压缩格式不能被分块,并行的处理,比如gzip。第二,另外的一些压缩格式虽然支持分块处理,但是解压的过程非常的缓慢,使job的瓶颈转移到了cpu上,例如bzip2。
如果能够拥有一种压缩算法,即能够被分块,并行的处理,速度也非常的快,那就非常的理想。这种方式就是lzo。
lzo的压缩文件是由许多的小的blocks组成(约256K),使的hadoop的job可以根据block的划分来split job。不仅如此,lzo在设计时就考虑到了效率问题,它的解压速度是gzip的两倍,这就让它能够节省很多的磁盘读写,它的压缩比的不如gzip,大约压缩出来的文件比gzip压缩的大一半,但是这样仍然比没有经过压缩的文件要节省20%-50%的存储空间,这样就可以在效率上大大的提高job执行的速度。
hadoop下lzo配置文档参考http://www.tech126.com/hadoop-lzo/

如何在MapReduce中使用压缩
1.输入的文件的压缩
如果输入的文件是压缩过的,那么在被MapReduce读取时,它们会被自动解压,根据文件扩展名来决定应该使用哪一个压缩解码器。
2.MapReduce作业的输出的压缩
如果要压缩MapReduce作业的输出,请在作业配置文件中将mapred.output.compress属性设置为true。将mapred.output.compression.codec属性设置为自己打算使用的压缩编码/解码器的类名。
如果为输出使用了一系列文件,可以设置mapred.output.compression.type属性来控制压缩类型,默认为RECORD,它压缩单独的记录。将它改为BLOCK,则可以压缩一组记录。由于它有更好的压缩比,所以推荐使用。
3.map作业输出结果的压缩
即使MapReduce应用使用非压缩的数据来读取和写入,我们也可以受益于压缩map阶段的中间输出。因为map作业的输出会被写入磁盘并通过网络传输到reducer节点,所以如果使用LZO之类的快速压缩,能得到更好的性能,因为传输的数据量大大减少了。以下代码显示了启用rnap输出压缩和设置压缩格式的配置属性。
[mw_shl_code=java,true]conf.setCompressMapOutput(true);
conf.setMapOutputCompressorClass(GzipCodec.class);[/mw_shl_code]
本地压缩库
考虑到性能,最好使用一个本地库(native library)来压缩和解压。例如,在一个测试中,使用本地gzip压缩库减少了解压时间50%,压缩时间大约减少了10%(与内置的Java实现相比较)。表4-4展示了Java和本地提供的每个压缩格式的实现。井不是所有的格式都有本地实现(例如bzip2压缩),而另一些则仅有本地实现(例如LZO)。
压缩格式 Java实现 本地实现
DEFLATE
gzip
bzip2
LZO



Hadoop带有预置的32位和64位Linux的本地压缩库,位于库/本地目录。对于其他平台,需要自己编译库,具体请参见Hadoop的维基百科http://wiki.apache.org/hadoop/NativeHadoop
本地库通过Java系统属性java.library.path来使用。Hadoop的脚本在bin目录中已经设置好这个属性,但如果不使用该脚本,则需要在应用中设置属性。
默认情况下,Hadoop会在它运行的平台上查找本地库,如果发现就自动加载。这意味着不必更改任何配置设置就可以使用本地库。在某些情况下,可能希望禁用本地库,比如在调试压缩相关问题的时候。为此,将属性hadoop.native.lib设置为false,即可确保内置的Java等同内置实现被使用(如果它们可用的话)。





回复

使用道具 举报

xukunddp 发表于 2013-10-25 10:45:23
2.2是在DFSClient做好压缩,再上传到DataNode的吧吗?
那么你显示的原始数据是哪个吗?  原始大小,还是压缩之后的大小吗?
回复

使用道具 举报

9528 发表于 2014-2-18 12:21:59
楼主能有个示例就功德无量了
回复

使用道具 举报

pig2 发表于 2014-2-18 16:39:12
9528 发表于 2014-2-18 12:21
楼主能有个示例就功德无量了

这里举一个例子:
 先说文件的压缩有两大好处:1、可以减少存储文件所需要的磁盘空间;2、可以加速数据在网络和磁盘上的传输。尤其是在处理大数据时,这两大好处是相当重要的。

  下面是一个使用gzip工具压缩文件的例子。将文件/user/hadoop/aa.txt进行压缩,压缩后为/user/hadoop/text.gz
  1. package com.hdfs;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.URI;
  6. import org.apache.hadoop.conf.Configuration;
  7. import org.apache.hadoop.fs.FSDataInputStream;
  8. import org.apache.hadoop.fs.FSDataOutputStream;
  9. import org.apache.hadoop.fs.FileSystem;
  10. import org.apache.hadoop.fs.Path;
  11. import org.apache.hadoop.io.IOUtils;
  12. import org.apache.hadoop.io.compress.CompressionCodec;
  13. import org.apache.hadoop.io.compress.CompressionCodecFactory;
  14. import org.apache.hadoop.io.compress.CompressionInputStream;
  15. import org.apache.hadoop.io.compress.CompressionOutputStream;
  16. import org.apache.hadoop.util.ReflectionUtils;
  17. public class CodecTest {
  18.     //压缩文件
  19.     public static void compress(String codecClassName) throws Exception{
  20.         Class<?> codecClass = Class.forName(codecClassName);
  21.         Configuration conf = new Configuration();
  22.         FileSystem fs = FileSystem.get(conf);
  23.         CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, conf);
  24.         //指定压缩文件路径
  25.         FSDataOutputStream outputStream = fs.create(new Path("/user/hadoop/text.gz"));
  26.         //指定要被压缩的文件路径
  27.         FSDataInputStream in = fs.open(new Path("/user/hadoop/aa.txt"));
  28.         //创建压缩输出流
  29.         CompressionOutputStream out = codec.createOutputStream(outputStream);  
  30.         IOUtils.copyBytes(in, out, conf);
  31.         IOUtils.closeStream(in);
  32.         IOUtils.closeStream(out);
  33.     }
  34.    
  35.     //解压缩
  36.     public static void uncompress(String fileName) throws Exception{
  37.         Class<?> codecClass = Class.forName("org.apache.hadoop.io.compress.GzipCodec");
  38.         Configuration conf = new Configuration();
  39.         FileSystem fs = FileSystem.get(conf);
  40.         CompressionCodec codec = (CompressionCodec)ReflectionUtils.newInstance(codecClass, conf);
  41.         FSDataInputStream inputStream = fs.open(new Path("/user/hadoop/text.gz"));
  42.          //把text文件里到数据解压,然后输出到控制台  
  43.         InputStream in = codec.createInputStream(inputStream);  
  44.         IOUtils.copyBytes(in, System.out, conf);
  45.         IOUtils.closeStream(in);
  46.     }
  47.    
  48.     //使用文件扩展名来推断二来的codec来对文件进行解压缩
  49.     public static void uncompress1(String uri) throws IOException{
  50.         Configuration conf = new Configuration();
  51.         FileSystem fs = FileSystem.get(URI.create(uri), conf);
  52.         
  53.         Path inputPath = new Path(uri);
  54.         CompressionCodecFactory factory = new CompressionCodecFactory(conf);
  55.         CompressionCodec codec = factory.getCodec(inputPath);
  56.         if(codec == null){
  57.             System.out.println("no codec found for " + uri);
  58.             System.exit(1);
  59.         }
  60.         String outputUri = CompressionCodecFactory.removeSuffix(uri, codec.getDefaultExtension());
  61.         InputStream in = null;
  62.         OutputStream out = null;
  63.         try {
  64.             in = codec.createInputStream(fs.open(inputPath));
  65.             out = fs.create(new Path(outputUri));
  66.             IOUtils.copyBytes(in, out, conf);
  67.         } finally{
  68.             IOUtils.closeStream(out);
  69.             IOUtils.closeStream(in);
  70.         }
  71.     }
  72.    
  73.     public static void main(String[] args) throws Exception {
  74.         //compress("org.apache.hadoop.io.compress.GzipCodec");
  75.         //uncompress("text");
  76.         uncompress1("hdfs://master:9000/user/hadoop/text.gz");
  77.     }
  78. }
复制代码
首先执行77行进行压缩,压缩后执行第78行进行解压缩,这里解压到标准输出,所以执行78行会再控制台看到文件/user/hadoop/aa.txt的内容。如果执行79行的话会将文件解压到/user/hadoop/text,他是根据/user/hadoop/text.gz的扩展名判断使用哪个解压工具进行解压的。解压后的路径就是去掉扩展名。

  进行文件压缩后,在执行命令./hadoop fs -ls /user/hadoop/查看文件信息,如下:
  1. [hadoop@master bin]$ ./hadoop fs -ls /user/hadoop/
  2. Found 7 items
  3. -rw-r--r--   3 hadoop supergroup   76805248 2013-06-17 23:55 /user/hadoop/aa.mp4
  4. -rw-r--r--   3 hadoop supergroup        520 2013-06-17 22:29 /user/hadoop/aa.txt
  5. drwxr-xr-x   - hadoop supergroup          0 2013-06-16 17:19 /user/hadoop/input
  6. drwxr-xr-x   - hadoop supergroup          0 2013-06-16 19:32 /user/hadoop/output
  7. drwxr-xr-x   - hadoop supergroup          0 2013-06-18 17:08 /user/hadoop/test
  8. drwxr-xr-x   - hadoop supergroup          0 2013-06-18 19:45 /user/hadoop/test1
  9. -rw-r--r--   3 hadoop supergroup         46 2013-06-19 20:09 /user/hadoop/text.gz
复制代码
回复

使用道具 举报

9528 发表于 2014-2-18 16:40:56
感谢楼主,受教了,我慢慢研究研究
回复

使用道具 举报

tang 发表于 2015-3-7 20:29:27
pig2 发表于 2014-2-18 16:39
这里举一个例子:
 先说文件的压缩有两大好处:1、可以减少存储文件所需要的磁盘空间;2、可以加速数据 ...

我觉得这个例子只是说明了 HDFS支持压缩,并没有解释清楚压缩2
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条