Hive的一个独特的功能就是:Hive不会强制要求将数据转换成特定的格式才能使用。
Hive利用Hadoop的InputFormatAPI来从不同的数据源读取数据,例如文本格式、sequence文件格式,甚至用户自定义格地。同样地,使用OutputFormat API也可以将数据写成不同的格式。
尽管Hadoop的文件系统支持对于非压缩数据的线性扩展存储,但是对数据进行压缩还是有很大好处的。压缩通常都会节约可观的磁盘空间,例如,基于文本的文件可以压缩4俨甚至更高比例。压缩同样可以增加吞吐量和性能。这看上去似乎并不合常理,因为压缩和解压缩会增加额外的CPU开销,不过,通过减少载人内存的数据量而提高I/O吞吐量会更加提高网络传输性能。
Hadoop的job通常是I/O密集型而不是CPU密集型的。如果是这样的话,压缩可以提高性能。不过,如果用户的job是CPU密集型的话,那么使用压缩可能会降低执行性能。确定是否进行压缩的唯一方法就是尝试不同的选择,并测量对比执行结果。

1、确定安装编解码器

基于用户所使用的 Hadoop 版本,会提供不同的编解码器。 Hive 中可以通过 set 命令查看 Hive 配皿文件中或 Hadoop 配里文件中配里的值。通过查看属性 io.compression.codec ,可以看到编解码器之间是按照逗号进行分割的:

【Hive】11-其他文件格式和压缩方法-LMLPHP

2、选择一种压缩编/解码器

使用压缩的优势是可以最小化所需要的磁盘存储空间,以及减小磁盘和网络IO 操作。不过,文件压缩过程和解压缩过程会增加 CPU 开销。因此,对于压缩密集型的 job 最好使用压缩,特别是有额外的 CPU 资源或磁盘存储空间比较稀缺的情况。所有最新的那些 Hadoop 版本都已经内置支持 GZIP 和 BZip2 压缩方案了,包括加速对这些格式的压缩和解压缩的本地 Liunx 库。绑定支持 SnapPy 压缩是最近才增加的,不过,如果用户当前使用的 Hadoop 版本不支持该功能的话,那么自行增加相关的库即可。另外,还有一种常用的压缩方案,即 LZO 压缩。

那么,为什么我们需要不同的压缩方案呢?每一个压缩方案都在压缩/解压缩速度和压缩率间进行权衡。 BZip2 压缩率最高,但是同时擂要消耗最多的 CPU 开销。 Gzip 是压缩率和压缩/解压缩速度上的下一个选择。因此,如果磁盘空间利用率和 IO 开销都需要考虑的话,那么这 2 种压缩方案都是有吸引力的。 LZO 和 Snappy 压缩率相比前面的 2 种要小但是压缩/解压缩速度要快,特别是解压缩过程。如果相对于磁盘空间和 IO 开销,频繁读取数据所有的解压缩速度更重要的话,那么它们将是不错的选择。

另一个而要考虑的因素是压缩格式的文件是否是可分割的。 MapReduce许要将非常大的输人文件分割成多个划分(通常一个文件块对应一个划分,也就是 128MB 的倍数) , 其中每个划分会被分发到一个单独的 map 进程中。只有当 Hadoop 知道文件中记录的边界时才可以进行这样的分割。对于文本文件,每一行都是一条记录,但是 Gzip 和 Snappy 将这些边界信息掩盖掉了。不过, Bzip2 和 LZO 提供了块( BLOCK)级别的压缩,也就是每个块中都含有完整的记录信息,因此 Hadoop 可以在块边界级别对这些文件进行划分。

虽然 Gzip 和 Snappy 压缩的文件不可划分,但是并不能因此而排除它们.当用户创建文件的时候,可以将文件分割成期望的文件大小。通常输出文件的个数等于 reducer 的个数。也就是说如果用户使用了 N 个 reducer ,那么通常就会得到 N 个输出文件。需要注意的是,如果有一个不可分割的文件特别的大,那么就会出现一个单独的 task 来读取整个文件,进行处理。

从 Hive的角度来看,对于文件格式,有 2 个方面的内容需要说明。一个方面是文件是怎样分隔成行(记录)的。文本文件使用场(换行符)作为默认的行分隔符。当用户没有使用默认的文本文件格式时,用户需要告诉 Hive 使用的 InputFomat 和 outputFormat 是什么。事实上,用户播要指定对于输人和输出格式实现的 Java 类的名称。 InPutFormat 中定义了如何读取划分,以及如何将划分分割成记录,而 OutPutFormat 中定义了如何将这些划分写回到文件或控制台输出中。

第 2 个方面是记录是如何分割成字段(或列)的。 Hive 使用^A 作文本文件中默认的字段分隔符。 Hive 使用 SerDe(也就是序列俗反序列化的简写)作为对输人记录 (反序列化)进行分割以及写记录(序列化)的“模板”。这时,用户只需要指定可以完成这 2 部分工作的一个 Java 类即可。

所有的这些信息用户在创建表的时候都可以在表定义语句中进行指定。创建完成后,用户可以像平时一样查询表,而无需关心底层格式。因此,如果用户是 Hive 的使用者,而不是 Java 工程师,就无需关心关于 Java 的信息。如果需要,用户所在团队的工程师可以帮忙指定需要的定义信息,而之后就可以像之前一样工作了。

3、开启中间压缩

对中间数据进行压缩可以减少 job 中 map和 reduce task 间的数据传输盘。对于中间数据压缩,选择一个低 CPU 开销的编/解码器要比选择一个压缩率高的编/解码器要重要得多。属性 hive.exec.compress.intermediate 的默认值是 false ,如果要开启中间压缩,就需要将这个属性值修改为默认值为 true :

【Hive】11-其他文件格式和压缩方法-LMLPHP

提示:对于其他 Hadoop job 来说控制中间数据压编的属性是 mapred.compress.map.output 。

Hadoop 压缩默认的编/解码器是 DefaultCodec .可以通过修改属性 mapred.map.output.compression.codec 的值来修改编/解码器。这是一个 Hadoop 配置项,可以在$HADOOP_HOME/conf/mapred-site.xml 文件中或$HIVE_HOME/conf/hive-site.xml 文件中进行配置。SnappyCodec 是一个比较好的中间文件压缩编/解码器,因为其很好地结合了低 CPU 开销和好的压缩执行效率:

【Hive】11-其他文件格式和压缩方法-LMLPHP

4、最终输出结果压缩

当 Hive 将输出写人到表中时,输出内容同样可以进行压缩。属性 hive.exec.compress.output 控制着这个功能。用户可能需要保持默认配置文件中的默认值 false , 这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true ,来开启输出结果压缩功能:

【Hive】11-其他文件格式和压缩方法-LMLPHP

提示:对于其他 Hadoop 任务,开启最终愉出结果压缩功能的属性是 mapred.output.compress。

如果属性hive.exec.compress.output 的值设置为true,那么这时需要为其指定一个编解码器。对于输出文件,使用 Gzip 进行压缩是个不错的主意,因为其通常可以大幅度降低文件的大小。但是,需要记住的是 Gzip 压缩的文件对于后面的 MapReduce job 而言是不可分割的。

【Hive】11-其他文件格式和压缩方法-LMLPHP

5、Sequencefile 存储格式

压缩文件确实能够节约存储空间,但是,在 Hadoop 中存储裸压缩文件的一个缺点就是,通常这些文件是不可分割的。可分割的文件可以划分成多个部分,由多个 mapper 并行进行处理。大多数的压缩文件是不可分割的。也就是说只能从头读到尾。 Hadoop 所支持的 sequence file 存储格式可以将一个文件划分成多个块,然后采用一种可分割的方式对块进行压缩。如果想在 Hivc 中使用sequence file 存储格式,那么需要在 CREATE TABLE 语句中通过 STORED AS SEQUENCEFILE语句进行指定:

CREATE TABLE a_sdequence_file_table STORED AS SEQUENCEFILE

Sequence file 提供3 种压缩方式: NONE 、 RECORD 和 BLOCK ,默认是 RECORD 级别(也就是记录级别)。不过,通常来说, BLOCK 级别(也就是块级别)压缩性能最好而且是可以分割的。和很多其他的压缩属性一样,这个属性也并非是 Hive 特有的。用户可以在 Hadoop 的 mapred-site.xml 文件中进行定义,或者在 Hive 的 hive-site.xml 文件中进行定义,需要的时候,还可以在脚本中或查询语句前进行指定:

【Hive】11-其他文件格式和压缩方法-LMLPHP

6、使用压缩实践

我们已经介绍了 Hive 中可以使用的一些压缩相关的配置属性,这些属性的不同组合方式将会产生不同的输出。下面,让我们在一些例子中使用这些属性,然后展示下它们的输出将会是什么。浦要注意的是, CLI 中通过对命令设置的属性在同一个会话中会一直生效的。因此,同一个会话中的不同例子间应该注意将设里复原,或者直接重开启一个 Hive 会话:

【Hive】11-其他文件格式和压缩方法-LMLPHP

首先,我们先开启中间数据压缩功能。这将不会影响到最终输出结果.不过从 job 的计数器信息上看,可以发现这个 job 中间传送的数据里变小了,因为 shuffle sort (混洗排序)数据被压缩了:

【Hive】11-其他文件格式和压缩方法-LMLPHP

和预期的一样,中间数据压缩没有影响到最终的输出,最终的数据结果仍然是非压缩的:

【Hive】11-其他文件格式和压缩方法-LMLPHP

我们同样可以为中间数据压缩配置其他的编/解码器而不使用默认的编/解码器。下面这个例子,我们选择使用 Gzip (尽管通常 Snappy 是更好的选择)。为显示清晰,将下面语句中的第 1 行分成了 2 行:

【Hive】11-其他文件格式和压缩方法-LMLPHP

下一步,我们可以开启输出结果:

【Hive】11-其他文件格式和压缩方法-LMLPHP

输出信息中的表统计信息显示 total_size (总大小)是 16B ,但是 raw_data_size (裸数据)是 6B ,多出的存储空间是被 deflate 算法消耗了。同时我们可以看到,输出的文件后缀名是.deflate不推荐使用 cat 命令来查着这个压缩文件,因为用户只能看到二进制输出。不过, Hive 可以正常地查询这个数据:

【Hive】11-其他文件格式和压缩方法-LMLPHP

这种无缝的处理压缩文件的能力并非是 Hive 独有的,实际上,这里是使用了 Hadoop 的 TextlnputFormat 进行的处理。尽管在这种情况下这个命名有些令人混淆,但是 TextlnputFormat 可以识别文件后缀名为.deflate 或gz 的压缩文件,并且可以很轻松地进行处理。 Hive 无需关心底层的文件是否是压缩的,以及是使用何种压缩方案进行压缩的。下面我们改变下输出结果压缩所使用的编解码器,然后看下结果(这里语句的第 2 行为了显示清晰,也分成 2 行了) :

【Hive】11-其他文件格式和压缩方法-LMLPHP

正如用户可以看到的,输出文件夹下现在包含了零个或多个.gz文件。 Hive 提供了在 Hive shell中执行像 zcat 这样的本地命令的快速方法。通过!符号, Hive 可以执行外部的命令,直到返回结果。 zcat 是一个命令行实用程序,用来解压缩,并进行输出显示:

【Hive】11-其他文件格式和压缩方法-LMLPHP

使用这种翰出压缩能够完成那种很小.操作起来很快的文件的二进制压缩。不过,回想一下,输出文件的个数是和处理数据所需要的 mapper 个数或 reducer 个数有关的。在最坏的情况下,可能最终结果是文件夹中只产生了一个大的压缩文件,而且是不可分割的。这意味着后续步骤并不能并行地处理这个数据。解决这个问题的答案就是使用 sequence file :

【Hive】11-其他文件格式和压缩方法-LMLPHP

Sequence file是二进制格式的,但是,我们可以很容易地查询文件头。通过查看文件头可以确认结果是否使我们需要的:

【Hive】11-其他文件格式和压缩方法-LMLPHP

因为 Sequence file 中嵌人的元数据信息以及 Hive 元数据信息,无需任何特别的设置就可以查询这张表。 Hadoop 同样提供了 dfs -text 命令来从sequence file文件中去掉文件头和压缩,然后显示裸数据: 

【Hive】11-其他文件格式和压缩方法-LMLPHP

最后,我们来同时使用直接数据压缩和最终输出数据压缩,而且使用不同压缩编/解码器的sequence file!这些设里通常应用在生产环境,这些环境中的数据集很大,而这些设里可以提高其性能:

【Hive】11-其他文件格式和压缩方法-LMLPHP

7、存档分区

Hadoop 中有一种存储格式名为 HAR ,也就是 Hadoop Archive ( Hadoop 归档文件)的简写。一个 HAR 文件就像在 HDFS 文件系统中的一个 TAR 文件一样是一个单独的文件。不过,其内部可以存放多个文件和文件夹。在一些使用场景下,较旧的文件夹和文件比较新的文件夹和文件被访问的概率要低很多。如果某个特定的分区下保存的文件有成千上万的话,那么就需要 HDFS 中的 NameNode 消耗非常大的代价来管理这些文件。通过将分区下的文件归档成一个巨大的,但是同时可以被 Hive 访问的文件,可以减轻 NameNode 的压力。不过其缺点是, HAR 文件查询效率不高.同时, HAR 文件并非是压缩的.因此也不会节约存储空间。

在下面的例子中,我们将使用 Hive 自带的文件作为表数据。首先,创建一个分区表,然后将 Hive 包中自带的文件加载到表中:

【Hive】11-其他文件格式和压缩方法-LMLPHP

某些 Hadoop (例如 Hadoop v0.20.2 版本)要求包含 Hadoop 归档工具接口的 JAR包要放置在 Hive 的 auxlib 路径下:

 【Hive】11-其他文件格式和压缩方法-LMLPHP

在归档之前,我们来看下这个表下面的底层目录结构.需要注意的是表下面的数据是存储在分区目录下的,因为这是一个管理分区表:

【Hive】11-其他文件格式和压缩方法-LMLPHP

ALTER TABLE … ARCHIVE PARTITION 语句将表转化成了一个归档表:

【Hive】11-其他文件格式和压缩方法-LMLPHP

(对于上面的输出,为便于展示,我们对其格式进行了调整,并使用 … 替换掉输出中的时间戮。)下面这张表中由之前的两个文件变成了现在的一个 Hadoop 归档文件(也就是 HAR 文件) :

【Hive】11-其他文件格式和压缩方法-LMLPHP

ALTER TABLE … UNARCHIVE PARTITION 命令可以将 HAR 中的文件提取出来然后重新放置到 HDFS 中:

ALTER TABLE hive_text UNARCHIVE PARTITION (folder = 'docs' );

8、压缩:包扎

Hive 可以读和写不同类型的压缩文件,确实可以获取很大的性能提升,因为其可以节约磁存储空间以及处理开销。这种灵活性同样也有助于和其他工具进行集成,因为 Hive 无裔使用 Java 编写的自定义的“适配器”就可以查询很多本地文件类型。

10-06 13:41