摘录自《Hadoop 权威指南》—— 第四版
文件压缩有两种好处:减少存储文件所需要的磁盘空间,并加速数据在网络和磁盘上的传输。这两大好处在处理数据时相当重要,值得仔细考虑在 Hadoop 中文件压缩的用法。
有很多种不同的压缩格式、工具和算法,它们各有千秋。如下表列出了与 Hadoop 结合使用的常见压缩方法:
压缩格式 | centered 工具 | 算法 | 文件扩展名 | 是否可切分 |
---|---|---|---|---|
DEFLATE | 无 | DEFLATE | .deflate | 否 |
gzip | gzip | DEFLATE | .gz | 否 |
bzip2 | bzip2 | bzip2 | .bz2 | 是 |
LZO | lzop | LZO | .lzo | 否 |
LZ4 | 无 | LZ4 | .lz4 | 否 |
Snappy | 无 | Snappy | .snappy | 否 |
所有压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。上表列出的所有压缩工具都提供 9 个不同的选项来控制压缩时必须考虑的权衡:选项 -1 为优化压缩速度,-9 为优化压缩空间。例如,下述命令通过最快的压缩方法创建一个名为 file.gz 的压缩文件:
%gzip -1 file
不同压缩工具有不同的压缩特性。gzip 是一个通用的压缩工具,在空间/时间性能的权衡中,居于其他两个压缩方法之间。bzip2 的压缩能力强于 gzip,但压缩速度更慢一些。尽管 bzip2 的解压速度比压缩速度快,但仍比其他压缩格式要慢一些。另一方面,LZO、LZ4 和 Snappy 均优化压缩速度,其速度比 gzip 快一个数量级,但压缩效率稍逊一筹。Snappy 和 LZ4 的解压速度比 LZO 高出很多。
上表中的“是否可切分”列表示对应的压缩算法是否支持切分(splitable),也就是说,是否可以搜索数据流的任意位置并进一步往下读区数据,可切分压缩格式尤其适合 MapReduce。
在考虑如何压缩将由 MapReduce 处理的数据时,理解这些压缩格式是否支持切分(splitting)是非常重要的。以一个存储在 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(http://code.google.com/p/hadoop-gpl-compression)或 GitHub(http://github.com/kevinweil/hadoop-lzo)中获得该类库。该工具构建了切分点索引,如果使用恰当的 MapReduce 输入格式可有效实现文件的可切分特性。
另一方面,bzip2 文件提供不同数据块之间的同步标识(pi 的 48 位近似值),因而它支持切分。
应该使用哪种压缩格式?
Hadoop 应用处理的数据集非常大,因此需要借助于压缩。使用哪种压缩格式与待处理的文件的大小、格式和所使用的工具相关。下面有一些建议。大致是按照效率从高到低排列的。
- 使用容器文件格式,例如顺序文件、Avro 数据文件、ORCFiles 或者 Parquet 文件,有这些文件格式同时支持压缩和切分。通常最好有一个快速压缩工具联合使用,例如 LZO、LZ4,或者 Snappy;
- 使用支持切分的压缩格式,例如 bzip2 (尽管 bzip2 非常慢),或者使用通过索引实现切分的压缩格式,例如 LZO;
- 在应用中将文件切分成块,并使用任意一种压缩格式为每个数据块建立压缩文件(不论它是否支持切分)。这种情况下,需要合理选择数据块的大小,以确保压缩后数据块的大小近似于 HDFS 块的大小;
- 存储未经压缩的文件;
对大文件来说,不要使用不支持切分整个文件的压缩格式,因为会失去数据的本地特性,进而造成 MapReduce 应用效率低下。