大数据:Hive - ORC 文件存储格式

1、ORC File文件结构

  ORC的全称是(Optimized Row Columnar),ORC文件格式是一种Hadoop生态圈中的列式存储格式,它的产生早在2013年初,最初产生自Apache Hive,用于下降Hadoop数据存储空间和加速Hive查询速度。和Parquet相似,它并非一个单纯的列式存储格式,仍然是首先根据行组分割整个表,在每个行组内进行按列存储。ORC文件是自描述的,它的元数据使用Protocol Buffers序列化,而且文件中的数据尽量的压缩以下降存储空间的消耗,目前也被Spark SQL、Presto等查询引擎支持,可是Impala对于ORC目前没有支持,仍然使用Parquet做为主要的列式存储格式。2015年ORC项目被Apache项目基金会提高为Apache顶级项目。ORC具备如下一些优点:html

  1. ORC是列式存储,有多种文件压缩方式,而且有着很高的压缩比。
  2. 文件是可切分(Split)的。所以,在Hive中使用ORC做为表的文件存储格式,不只节省HDFS存储资源,查询任务的输入数据量减小,使用的MapTask也就减小了。
  3. 提供了多种索引,row group index、bloom filter index。
  4. ORC能够支持复杂的数据结构(好比Map等)

 列式存储  

  因为OLAP查询的特色,列式存储能够提高其查询性能,可是它是如何作到的呢?这就要从列式存储的原理提及,从图1中能够看到,相对于关系数据库中一般使用的行式存储,在使用列式存储时每一列的全部元素都是顺序存储的。由此特色能够给查询带来以下的优化:算法

  • 查询的时候不须要扫描所有的数据,而只须要读取每次查询涉及的列,这样能够将I/O消耗下降N倍,另外能够保存每一列的统计信息(min、max、sum等),实现部分的谓词下推。
  • 因为每一列的成员都是同构的,能够针对不一样的数据类型使用更高效的数据压缩算法,进一步减少I/O。
  • 因为每一列的成员的同构性,可使用更加适合CPU pipeline的编码方式,减少CPU的缓存失效。

  

 

关于Orc文件格式的官网介绍,见:数据库

        https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORCapache

  须要注意的是,ORC在读写时候须要消耗额外的CPU资源来压缩和解压缩,固然这部分的CPU消耗是很是少的。

数组

  数据模型

  和Parquet不一样,ORC原生是不支持嵌套数据格式的,而是经过对复杂数据类型特殊处理的方式实现嵌套格式的支持,例如对于以下的hive表:缓存

CREATE TABLE `orcStructTable`( `name` string, `course` struct<course:string,score:int>, `score` map<string,int>, `work_locations` array<string> )

 

  在ORC的结构中包含了复杂类型列和原始类型,前者包括LIST、STRUCT、MAP和UNION类型,后者包括BOOLEAN、整数、浮点数、字符串类型等,其中STRUCT的孩子节点包括它的成员变量,可能有多个孩子节点,MAP有两个孩子节点,分别为key和value,LIST包含一个孩子节点,类型为该LIST的成员类型,UNION通常不怎么用获得。每个Schema树的根节点为一个Struct类型,全部的column按照树的中序遍历顺序编号。性能优化

ORC只须要存储schema树中叶子节点的值,而中间的非叶子节点只是作一层代理,它们只须要负责孩子节点值得读取,只有真正的叶子节点才会读取数据,而后交由父节点封装成对应的数据结构返回。数据结构

  文件结构

  和Parquet相似,ORC文件也是以二进制方式存储的,因此是不能够直接读取,ORC文件也是自解析的,它包含许多的元数据,这些元数据都是同构ProtoBuffer进行序列化的。ORC的文件结构以下图,其中涉及到以下的概念:app

  • ORC文件:保存在文件系统上的普通二进制文件,一个ORC文件中能够包含多个stripe,每个stripe包含多条记录,这些记录按照列进行独立存储,对应到Parquet中的row group的概念。
  • 文件级元数据:包括文件的描述信息PostScript、文件meta信息(包括整个文件的统计信息)、全部stripe的信息和文件schema信息。
  • stripe:一组行造成一个stripe,每次读取文件是以行组为单位的,通常为HDFS的块大小,保存了每一列的索引和数据。
  • stripe元数据:保存stripe的位置、每个列的在该stripe的统计信息以及全部的stream类型和位置。
  • row group:索引的最小单位,一个stripe中包含多个row group,默认为10000个值组成。
  • stream:一个stream表示文件中一段有效的数据,包括索引和数据两类。索引stream保存每个row group的位置和统计信息,数据stream包括多种类型的数据,具体须要哪几种是由该列类型和编码方式决定。

  在ORC文件中保存了三个层级的统计信息,分别为文件级别、stripe级别和row group级别的,他们均可以用来根据Search ARGuments(谓词下推条件)判断是否能够跳过某些数据,在统计信息中都包含成员数和是否有null值,而且对于不一样类型的数据设置一些特定的统计信息。ide

(1)file level
  在ORC文件的末尾会记录文件级别的统计信息,会记录整个文件中columns的统计信息。这些信息主要用于查询的优化,也能够为一些简单的聚合查询好比max, min, sum输出结果。 

(2)stripe level
  ORC文件会保存每一个字段stripe级别的统计信息,ORC reader使用这些统计信息来肯定对于一个查询语句来讲,须要读入哪些stripe中的记录。好比说某个stripe的字段max(a)=10,min(a)=3,那么当where条件为a >10或者a <3时,那么这个stripe中的全部记录在查询语句执行时不会被读入。 

(3)row level 
  为了进一步的避免读入没必要要的数据,在逻辑上将一个column的index以一个给定的值(默认为10000,可由参数配置)分割为多个index组。以10000条记录为一个组,对数据进行统计。Hive查询引擎会将where条件中的约束传递给ORC reader,这些reader根据组级别的统计信息,过滤掉没必要要的数据。若是该值设置的过小,就会保存更多的统计信息,用户须要根据本身数据的特色权衡一个合理的值

 

  ORC元数据

  请参考:更高的压缩比,更好的性能–使用ORC文件格式优化Hive

  数据访问

  读取ORC文件是从尾部开始的,第一次读取16KB的大小,尽量的将Postscript和Footer数据都读入内存。文件的最后一个字节保存着PostScript的长度,它的长度不会超过256字节,PostScript中保存着整个文件的元数据信息,它包括文件的压缩格式、文件内部每个压缩块的最大长度(每次分配内存的大小)、Footer长度,以及一些版本信息。在Postscript和Footer之间存储着整个文件的统计信息(上图中未画出),这部分的统计信息包括每个stripe中每一列的信息,主要统计成员数、最大值、最小值、是否有空值等。

  接下来读取文件的Footer信息,它包含了每个stripe的长度和偏移量,该文件的schema信息(将schema树按照schema中的编号保存在数组中)、整个文件的统计信息以及每个row group的行数。

  处理stripe时首先从Footer中获取每个stripe的其实位置和长度、每个stripe的Footer数据(元数据,记录了index和data的的长度),整个striper被分为index和data两部分,stripe内部是按照row group进行分块的(每个row group中多少条记录在文件的Footer中存储),row group内部按列存储。每个row group由多个stream保存数据和索引信息。每个stream的数据会根据该列的类型使用特定的压缩算法保存。在ORC中存在以下几种stream类型:

  • PRESENT:每个成员值在这个stream中保持一位(bit)用于标示该值是否为NULL,经过它能够只记录部位NULL的值
  • DATA:该列的中属于当前stripe的成员值。
  • LENGTH:每个成员的长度,这个是针对string类型的列才有的。
  • DICTIONARY_DATA:对string类型数据编码以后字典的内容。
  • SECONDARY:存储Decimal、timestamp类型的小数或者纳秒数等。
  • ROW_INDEX:保存stripe中每个row group的统计信息和每个row group起始位置信息。

  在初始化阶段获取所有的元数据以后,能够经过includes数组指定须要读取的列编号,它是一个boolean数组,若是不指定则读取所有的列,还能够经过传递SearchArgument参数指定过滤条件,根据元数据首先读取每个stripe中的index信息,而后根据index中统计信息以及SearchArgument参数肯定须要读取的row group编号,再根据includes数据决定须要从这些row group中读取的列,经过这两层的过滤须要读取的数据只是整个stripe多个小段的区间,而后ORC会尽量合并多个离散的区间尽量的减小I/O次数。而后再根据index中保存的下一个row group的位置信息调至该stripe中第一个须要读取的row group中。

  ORC文件格式只支持读取指定字段,还不支持只读取特殊字段类型中的指定部分。 

  使用ORC文件格式时,用户可使用HDFS的每个block存储ORC文件的一个stripe。对于一个ORC文件来讲,stripe的大小通常须要设置得比HDFS的block小,若是不这样的话,一个stripe就会分别在HDFS的多个block上,当读取这种数据时就会发生远程读数据的行为。若是设置stripe的只保存在一个block上的话,若是当前block上的剩余空间不足以存储下一个strpie,ORC的writer接下来会将数据打散保存在block剩余的空间上,直到这个block存满为止。这样,下一个stripe又会从下一个block开始存储。

  因为ORC中使用了更加精确的索引信息,使得在读取数据时能够指定从任意一行开始读取,更细粒度的统计信息使得读取ORC文件跳过整个row group,ORC默认会对任何一块数据和索引信息使用ZLIB压缩,所以ORC文件占用的存储空间也更小,这点在后面的测试对比中也有所印证。

 

关于row group index和bloom filter index的性能优化,请参考Hive性能优化之ORC索引–Row Group Index vs Bloom Filter Index

 

文件压缩

  ORC文件使用两级压缩机制,首先将一个数据流使用流式编码器进行编码,而后使用一个可选的压缩器对数据流进行进一步压缩。 
  一个column可能保存在一个或多个数据流中,能够将数据流划分为如下四种类型: 
• Byte Stream 
  字节流保存一系列的字节数据,不对数据进行编码。 

• Run Length Byte Stream 
  字节长度字节流保存一系列的字节数据,对于相同的字节,保存这个重复值以及该值在字节流中出现的位置。 

• Integer Stream 
  整形数据流保存一系列整形数据。能够对数据量进行字节长度编码以及delta编码。具体使用哪一种编码方式须要根据整形流中的子序列模式来肯定。 

• Bit Field Stream 
  比特流主要用来保存boolean值组成的序列,一个字节表明一个boolean值,在比特流的底层是用Run Length Byte Stream来实现的。 

  接下来会以Integer和String类型的字段举例来讲明。 

(1)Integer 
  对于一个整形字段,会同时使用一个比特流和整形流。比特流用于标识某个值是否为null,整形流用于保存该整形字段非空记录的整数值。 

(2)String 
  对于一个String类型字段,ORC writer在开始时会检查该字段值中不一样的内容数占非空记录总数的百分比不超过0.8的话,就使用字典编码,字段值会保存在一个比特流,一个字节流及两个整形流中。比特流也是用于标识null值的,字节流用于存储字典值,一个整形流用于存储字典中每一个词条的长度,另外一个整形流用于记录字段值。 

  若是不能用字典编码,ORC writer会知道这个字段的重复值太少,用字典编码效率不高,ORC writer会使用一个字节流保存String字段的值,而后用一个整形流来保存每一个字段的字节长度。 

  在ORC文件中,在各类数据流的底层,用户能够自选ZLIB, Snappy和LZO压缩方式对数据流进行压缩。编码器通常会将一个数据流压缩成一个个小的压缩单元,在目前的实现中,压缩单元的默认大小是256KB。

  参数

  参数可参看:https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties#ConfigurationProperties-ORCFileFormat

 

2、Hive+ORC创建数据仓库

在建Hive表的时候咱们就应该指定文件的存储格式。因此你能够在Hive QL语句里面指定用ORCFile这种文件格式,以下:

CREATE TABLE ... STORED AS ORC ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT ORC SET hive.default.fileformat=Orc

全部关于ORCFile的参数都是在Hive QL语句的TBLPROPERTIES字段里面出现,他们是:

Key

Default

Notes

orc.compress

ZLIB

high level compression (one of NONE, ZLIB, SNAPPY)

orc.compress.size

262,144

number of bytes in each compression chunk

orc.stripe.size

268435456

number of bytes in each stripe

orc.row.index.stride

10,000

number of rows between index entries (must be >= 1000)

orc.create.index

true

whether to create row indexes

 3、Java操做ORC

  到https://orc.apache.org官网下载orc源码包,而后编译获取orc-core-1.3.0.jar、orc-mapreduce-1.3.0.jar、orc-tools-1.3.0.jar,将其加入项目中

import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; import org.apache.orc.CompressionKind; import org.apache.orc.OrcFile; import org.apache.orc.TypeDescription; import org.apache.orc.Writer; public class TestORCWriter { public static void main(String[] args) throws Exception { Path testFilePath = new Path("/tmp/test.orc"); Configuration conf = new Configuration(); TypeDescription schema = TypeDescription.fromString("struct<field1:int,field2:int,field3:int>"); Writer writer = OrcFile.createWriter(testFilePath, OrcFile.writerOptions(conf).setSchema(schema).compress(CompressionKind.SNAPPY)); VectorizedRowBatch batch = schema.createRowBatch(); LongColumnVector first = (LongColumnVector) batch.cols[0]; LongColumnVector second = (LongColumnVector) batch.cols[1]; LongColumnVector third = (LongColumnVector) batch.cols[2]; final int BATCH_SIZE = batch.getMaxSize(); // add 1500 rows to file
        for (int r = 0; r < 15000000; ++r) { int row = batch.size++; first.vector[row] = r; second.vector[row] = r * 3; third.vector[row] = r * 6; if (row == BATCH_SIZE - 1) { writer.addRowBatch(batch); batch.reset(); } } if (batch.size != 0) { writer.addRowBatch(batch); batch.reset(); } writer.close(); } }

大多状况下,仍是建议在Hive中将文本文件转成ORC格式,这种用JAVA在本地生成ORC文件,属于特殊需求场景。

 

参考:

http://lxw1234.com/archives/2016/04/630.htm

https://www.iteblog.com/archives/1014.html

http://blog.csdn.net/dabokele/article/details/51542327

http://blog.csdn.net/dabokele/article/details/51813322

http://blog.csdn.net/nysyxxg/article/details/52241848

http://blog.csdn.net/yu616568/article/details/51868447

相关文章
相关标签/搜索