Hive笔记整理之——文件存储格式

来源:互联网 发布:热血无赖mac存档位置 编辑:程序博客网 时间:2024/06/08 09:53

      • Hive的文件存储格式
      • RCFile和ORCFile详解
        • RCFile 的设计和实现
        • ORC File
      • 参考

Hive的文件存储格式

  1. textfile

    textfile为默认格式
    存储方式:行存储
    磁盘开销大 数据解析开销大
    压缩的text文件 hive无法进行合并和拆分
    textfile 存储空间消耗比较大,并且压缩的text 无法分割和合并 查询的效率最低,可以直接存储,加载数据的速度最高

  2. sequencefile

    二进制文件,以key-value的形式序列化到文件中
    存储方式:行存储
    可分割 压缩
    一般选择block压缩
    优势是文件和hadoop api中的mapfile是相互兼容的。

    sequencefile 存储空间消耗最大,压缩的文件可以分割和合并 查询效率高,需要通过text文件转化来加载

  3. rcfile

    存储方式:数据按行分块 每块按照列存储
    压缩快 快速列存取
    读记录尽量涉及到的block最少
    读取需要的列只需要读取每个row group 的头部定义。
    读取全量数据的操作 性能可能比sequencefile没有明显的优势

    rcfile 存储空间最小,查询的效率最高 ,需要通过text文件转化来加载,加载的速度最低

  4. orc

    存储方式:数据按行分块 每块按照列存储
    压缩快 快速列存取
    效率比rcfile高,是rcfile的改良版本

    最优使用方案

  5. 自定义格式

    用户可以通过实现inputformat和 outputformat来自定义输入输出格式。

RCFile和ORCFile详解

​ RCFile 在读取数据时可以跳过不需要的列,不需要将一整行读入然后选择所需字段,所以在 Hive 中执行 select a, b from tableA where c = 1 这样的操作就相对比较高效。

关于 RCFile 的论文(RCFile: A Fast and Space-efficient Data Placement Structure in MapReduce-based Warehouse System)

首先大数据的查询和处理有以下几个需求:

  1. 数据的快速加载
  2. 数据的快速查询处理
  3. 高效的存储,减少存储空间的使用
  4. 能很好的适应动态的查询模式

在传统的数据库系统中,主要有三种数据存储方式:

  1. 水平的行存储结构: 行存储模式就是把一整行存在一起,包含所有的列,这是最常见的模式。这种结构能很好的适应动态的查询,比如 select a from tableAselect a, b, c, d, e, f, g from tableA 这样两个查询其实查询的开销差不多,都需要把所有的行读进来过一遍,拿出需要的列。而且这种情况下,属于同一行的数据都在同一个 HDFS 块上,重建一行数据的成本比较低。但是这样做有两个主要的弱点:a)当一行中有很多列,而我们只需要其中很少的几列时,我们也不得不把一行中所有的列读进来,然后从中取出一些列。这样大大降低了查询执行的效率。b)基于多个列做压缩时,由于不同的列数据类型和取值范围不同,压缩比不会太高。
  2. 垂直的列存储结构: 列存储是将每列单独存储或者将某几个列作为列组存在一起。列存储在执行查询时可以避免读取不必要的列。而且一般同列的数据类型一致,取值范围相对多列混合更小,在这种情况下压缩数据能达到比较高的压缩比。但是这种结构在重建出行时比较费劲,尤其适当一行的多个列不在一个 HDFS 块上的时候。 比如我们从第一个 DataNode 上拿到 column A,从第二个 DataNode 上拿到了 column B,又从第三个 DataNode 上拿到了 column C,当要把 A,B,C 拼成一行时,就需要把这三个列放到一起重建出行,需要比较大的网络开销和运算开销。
  3. 混合的 PAX 存储结构: PAX 结构是将行存储和列存储混合使用的一种结构,主要是传统数据库中提高 CPU 缓存利用率的一种方法,并不能直接用到 HDFS 中。但是 RCFile 也是继承自它的思想,先按行存再按列存。

RCFile 的设计和实现


RCFile文件格式是FaceBook开源的一种Hive的文件存储格式,首先将表分为几个行组,对每个行组内的数据进行按列存储,每一列的数据都是分开存储,正是先水平划分,再垂直划分的理念。

在存储结构上:
如上图是HDFS内RCFile的存储结构,我们可以看到,首先对表进行行划分,分成多个行组。一个行组主要包括:16字节的HDFS同步块信息,主要是为了区分一个HDFS块上的相邻行组;元数据的头部信息主要包括该行组内的存储的行数、列的字段信息等等;数据部分我们可以看出RCFile将每一行,存储为一列,将一列存储为一行,因为当表很大,我们的字段很多的时候,我们往往只需要取出固定的一列就可以。

在一般的列存储中,会将不同的列分开存储,这样在查询的时候会跳过某些列,但是有时候存在一个表的有些列不在同一个HDFS块上(如下图),所以在查询的时候,hive重组列的过程会浪费很多IO开销。

  1. 数据的布局: 首先根据 HDFS 的结构,一个表可以由多个 HDFS 块构成。在每个 HDFS 块中,RCFile 以 row group 为基本单位组织数据,一个表多所有 row group 大小一致,一个 HDFS 块中可以包含多个 row group。每个 row group 包含三个部分,第一部分是 sync marker,用来区分一个 HDFS 块中两个连续多 row group。第二部分是 row groupmetadata header,记录每个 row group 中有多少行数据,每个列数据有多少字节,以及每列一行数据的字节数。第三部分就是 row group 中的实际数据,这里是按列存储的。
  2. 数据的压缩:metadata header 部分用 RLE (Run Length Encoding) 方法压缩,因为对于记录每个列数据中一行数据的字节数是一样的,这些数字重复连续出现,因此用这种方法压缩比比较高。在压缩实际数据时,每列单独压缩。
  3. 数据的追加写: RCFile 会在内存里维护每个列的数据,叫 column holder,当一条记录加入时,首先会被打散成多个列,人后追加到每列对应的 column holder,同时更新 metadata header 部分。可以通过记录数或者缓冲区大小控制内存中 column holder 的大小。当记录数或缓冲大小超过限制,就会先压缩 metadata header,再压缩 column holder,然后写到 HDFS。
  4. 数据的读取和惰性解压(lazy decompression): RCFile 在处理一个 row group 时,只会读取 metadata header 和需要的列,合理利用列存储在 I/O 方面的优势。而且即使在查询中出现的列技术读进内存也不一定会被解压缩,只有但确定该列数据需要用时才会去解压,也就是惰性解压(lazy decompression)。例如对于 select a from tableA where b = 1,会先解压 b 列,如果对于整个 row group 中的 b 列,值都不为 1,那么就没必要对这个 row group 对 a 列去解压,因为整个 row group 都跳过了。
  5. row group 的大小: row group 太小肯定是不能充分发挥列存储的优势,但是太大也会有问题。首先,论文中实验指出,当 row group 超过某一阈值时,很难再获得更高当压缩比。其次,row group 太大会降低 lazy decompression 带来的益处,还是拿 select a from tableA where b = 1 来说,如果一个 row group 里有一行 b = 1,我们还是要解压 a 列,从而根据 metadata header 中的信息找到 b = 1 的那行的 a 列的值,如果此时我们把 row group 设小一点,假设就设成 1,这样对于 b <> 1 的行都不需要解压 a 列。最后论文中给出一个一般性的建议,建议将 row group 设成 4MB。

ORC File


然后是 ORC File(Optimized Row Columnar file),对RCFile做了一些优化,克服 RCFile 的一些限制,主要参考这篇文档。
和RCFile格式相比,ORC File格式有以下优点:

  • 每个task只输出单个文件,这样可以减少NameNode的负载;
  • 支持各种复杂的数据类型,比如: datetime, decimal, 以及一些复杂类型(struct, list, map, and union);
  • 在文件中存储了一些轻量级的索引数据;
  • 基于数据类型的块模式压缩:
  • a、integer类型的列用游程编码(run-length encoding)
  • String类型的列用字典编码(dictionary encoding);
  • 用多个互相独立的RecordReaders并行读相同的文件;
  • 无需扫描markers就可以分割文件;
  • 绑定读写所需要的内存;
  • metadata的存储是用 Protocol Buffers的,所以它支持添加和删除一些列。

存储结构上
根据结构图,我们可以看到ORCFile在RCFile基础上引申出来Stripe和Footer等。每个ORC文件首先会被横向切分成多个Stripe,而每个Stripe内部以列存储,所有的列存储在一个文件中,而且每个stripe默认的大小是250MB,相对于RCFile默认的行组大小是4MB,所以比RCFile更高效。

文件结构

ORC File包含一组组的行数据,称为stripes,除此之外,ORC File的file footer还包含一些额外的辅助信息。在ORC File文件的最后,有一个被称为postscript的区,它主要是用来存储压缩参数及压缩页脚的大小。

在默认情况下,一个stripe的大小为250MB。大尺寸的stripes使得从HDFS读数据更高效。

在file footer里面包含了该ORC File文件中stripes的信息,每个stripe中有多少行,以及每列的数据类型。当然,它里面还包含了列级别的一些聚合的结果,比如:count, min, max, and sum。

Postscripts中存储该表的行数,压缩参数,压缩大小,列等信息Stripe Footer中包含该stripe的统计结果,包括MaxMin,count等信息FileFooter中包含该表的统计结果,以及各个Stripe的位置信息IndexData中保存了该stripe上数据的位置信息,总行数等信息RowData以stream的形式保存了数据的具体信息

存储空间上**
ORCFile扩展了RCFile的压缩,除了Run-length(游程编码),引入了字典编码和Bit编码。
采用字典编码,最后存储的数据便是
字典中的值,每个字典值得长度以及字段在字典中的位置

至于Bit编码,对所有字段都可采用Bit编码来判断该列是否为null,
如果为null则Bit值存为0,否则存为1,对于为null的字段在实际编码的时候不需要存储,也就是说字段若为null,是不占用存储空间的。

Stripe结构

每个Stripe都包含index data、row data以及stripe footer,Stripe footer包含流位置的目录,Row data在表扫描的时候会用到。

Index data包含每列的最大和最小值以及每列所在的行。行索引里面提供了偏移量,它可以跳到正确的压缩块位置。

通过行索引,可以在stripe中快速读取的过程中可以跳过很多行,尽管这个stripe的大小很大。在默认情况下,最大可以跳过10000行。

因为可以通过过滤预测跳过很多行,因而可以在表的 secondary keys 进行排序,从而可以大幅减少执行时间。比如你的表的主分区是交易日期,那么你可以对次分区(state、zip code以及last name)进行排序。

Hive读取数据的时候,根据FileFooter读出Stripe的信息,根据IndexData读出数据的偏移量从而读取出数据。
网友有一幅图,形象的说明了这个问题:

参考:

http://blog.csdn.net/u014307117/article/details/52381383

http://blog.sina.com.cn/s/blog_7e04e0d00102vx5v.html