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
因为OLAP查询的特色,列式存储能够提高其查询性能,可是它是如何作到的呢?这就要从列式存储的原理提及,从图1中能够看到,相对于关系数据库中一般使用的行式存储,在使用列式存储时每一列的全部元素都是顺序存储的。由此特色能够给查询带来以下的优化:算法
关于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文件中保存了三个层级的统计信息,分别为文件级别、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文件格式优化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类型:
在初始化阶段获取所有的元数据以后,能够经过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。
在建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 |
到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