HDFS文件的压缩

前言

新搭建的集群,需要进行各种测试,于是让一个同事导入一些测试数据,大约1.4T左右。我进行了一个简单的hive测试

//故意增加shuffle
SELECT DISTINCT(COUNT id) FROM test_table;

谁知道…就这么一个简单的SQL,半个小时过去后,仍然没有出结果

追查原因

原来同事从ftp拉取过来的数据是gzip格式的,他直接导入到Hive表中…,而gzip格式的数据是不可切分的

如何理解不可切分

为了进一步理解gzip等不可切分的压缩格式,我做了个测试

  • 上传测试数据
    《HDFS文件的压缩》
    说明:
    使用linux自带的gzip将testdata(拷贝了bigtable的数据,因为直接gzip bigtable,会删除原始数据)
    或者使用:
    《HDFS文件的压缩》
    说明:
    这样不但可以将原有的文件保留,而且可以将压缩包放到任何目录中,解压缩也一样可以看到bigtable.gz和testdata.gz大小一致

  • 在hdfs根目录创建一个data目录、

[root@node02 data]# hadoop fs -mkdir /data
[root@node02 data]# hadoop fs -ls /
Found 2 items
drwxr-xr-x   - root supergroup          0 2020-01-04 05:47 /data
drwxrwxrwx   - root supergroup          0 2019-06-26 06:23 /tmp
  • 上传压缩后的数据
    由于数据太小(大约50M),仅有一个block,为了测试,自定义block块为10M:
hadoop fs -D dfs.blocksize=10485760 -put bigtable.gz /data

《HDFS文件的压缩》
《HDFS文件的压缩》
《HDFS文件的压缩》

从上图中明显可以看到:gzip格式的数据,不支持切分的真正含义,并不是说HDFS不会将文件分布式的存储在各个节点,而是在计算的时候,不支持切分,也就是仅仅有一个split,从而也就是仅有一个map,这样的效率是及其低下的。
(后面还会更详细的讲解)
正常支持切分的数据,执行流程如下:

《HDFS文件的压缩》
而不持支切分的数据,如gzip格式的数据,就只有一个map:
《HDFS文件的压缩》
这样直接导致的结果就是:
一个map任务处理N个HDFS块,而其中大多数块并没有存储在执行该map任务的节点上,执行的效率会非常低

既然这样,难道压缩数据就没有意义了吗?当然不是,而且在生产环境中数据都是经过压缩处理的,接下来详细讲解HDFS数据的压缩

详解HDFS文件压缩

  • 文件压缩的两大好处
    (1)减少存储文件所需要的磁盘空间
    (2)加速数据在网络和磁盘上的传输

  • 与Hadoop结合使用的常见压缩方法

压缩格式工具算法文件扩展名是否可切分
DEFLATEDEFLATE.deflate
gzipgzipDEFLATE.gz
bzip2bzip2bzip2.bz2
LZOlzopLZO.lzo
LZ4LZ4.lz4
SnappySnappy.snappy

说明:

  1. DEFLATE是一个标准压缩算法,该算法的标准实现是zlib。没有可用于生成DEFLATE文件的常用命令行工具,因为通常都用gzip格式。注意,gzip文件格式只是在DEFLATE格式上增加了文件头和一个文件尾。.deflate扩展名是Hadoop约定的。
  2. 如果LZO文件已经在预处理过程中被索引了,那么LZO文件是可切分的。
  • 详细描述文件的压缩
    所有压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。上表列出所有压缩工具都提供9个不同选项来控制压缩时必须考虑的权衡:选项-1为优化压缩速度,-9为优化压缩空间。例如,下述命令通过最快压缩方法创建一个名为file.gz的压缩文件:
    gzip -1 file
    不同压缩工具有不同的压缩特性。gzip是一个通用的压缩工具,在空间/时间性能的权衡中,属于其他两个压缩方法之间。bzip2的压缩能力强于gzip(即省空间),但压缩速度更慢一点。尽管gzip的解压速度比要锁速度快,但仍比其他压缩格式要慢一些。另一方面,LZO、LZ4和Snappy均优化压缩速度,其速度比gzip快一个数据量级,但压缩效率稍逊一筹。Snappy和LZ4的解压缩速度比LZO高出很多,通过下面的一张图更形象的看出:压缩比和压缩速度是相反的
    《HDFS文件的压缩》
    上表中的“是否可切分”列表示对应的压缩算法是否支持切分(splitable),也就是说,是否可以搜素数据流的任意位置并进一步往下读取数据。可切分压缩格式尤其适合MapReduce(这个原因上面有讲解,下面进行更详细的描述)

更详细的描述压缩和输入分片

在考虑如何压缩将有MapReduce处理的数据时,理解这些压缩格式是否支持切分(splitable)是非常重要的。以一个存储在HDFS文件系统中且压缩前大小为1GB的文件为例。如果HDFS的块大小设置为128MB,那么该文件将被存储在8个块中,把这个文件作为输入数据的MapReduce作业,将创建8个输入分片,其中每个分片作为一个单独的map任务的输入被独立处理。

现在想象一下,文件是经过gzip压缩的,且压缩后文件大小为1GB。与以前一样,HDFS将这个文件保存为8个数据块。但是,将每个数据块单独作为一个输入分片是无法实现工作的,因为无法实现从gzip压缩数据流的任意位置读取数据,所以让map任务独立与其他任务进行数据读取是行不通的。gzip格式使用DEFLATE算法来存储压缩后的数据,而DEFLATE算法将数据存储在一系列连续的压缩块中。问题在于每个块的起始位置并没有以任何形式标记,所以读取时无法从数据流的任意当前位置前进到下一块的起始位置读取下一个数据块,从而实现与整个数据流的同步。由于上述原因,gzip并不支持文件切分。

在这种情况下,MapReduce会采用正确的做法,它不会尝试切分gzip压缩文件,因为它知道输入的时gzip压缩文件(通过文件扩展名看出)且gzip不持支切分。这是可行的,但牺牲了数据的本地性:一个map任务处理8个HDFS块,而其中大多数块并没有存储在执行该map任务的节点上。而且,map任务数量越少,作业的粒度就越大,因而运行的时间可能会更长。

在前面假设的例子中,如果文件时通过LZO压缩的,我们会面临相同的问题,因为这个压缩格式也不支持数据读取和数据流同步。但是,在预处理LZO文件的时候使用包含在Hadoop LZO库文件中的索引工具时可能的,你可以在Google和Github网站上获得该库。该工具构建了切分点索引,如果使用恰当的MapReduce输入格式可有效实现文件的可切分特性。

另一方面,bzip2文件提供不同数据块之间的同步标识(pi的48位近似值),因而它支持切分。可以参考前面的表,了解每个压缩格式是否支持可切分

该使用哪种压缩格式

Hadoop应用处理的数据集非常大,因此需要借助与压缩。使用哪种压缩格式,与待处理的文件的大小、格式和所使用的工具有关。下面有一些建议,大致是按照效率从高到底排列的。

  • 使用容器文件格式,例如顺序文件(Sequence File)、Avro数据文件、ORCFile或者Parquet文件,所有这些文件格式同时支持压缩和切分。通常最好与一个快速压缩工具联合使用,例如,LZO、LZ4或者Snappy
  • 使用支持切分的压缩格式,例如bzip2(尽管bzip2非常慢),或者使用通过索引实现切分的压缩格式,例如LZO
  • 在应用中将文件切分成块,并使用任意一种压缩格式为每个数据块建立压缩文件(不论它是否支持切分)。这种情况下,需要合理选择数据块大小,以确保压缩后的数据块的大小近似于HDFS块的大小
  • 存储未经压缩的文件

对于大文件来说,不要使用不支持切分整个文件的压缩格式,因为会失去数据的本地性,进而造成MapReduce应用效率低下

后记

本文内容参考Hadoop权威指南

    原文作者:SunnyRivers
    原文地址: https://blog.csdn.net/Android_xue/article/details/103831799
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞