一、ORC File文件构造

  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在读写时候须要耗费额定的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的文件构造如下图,其中波及到如下的概念:
• 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值,并且对于不同类型的数据设置一些特定的统计信息。
(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文件是从尾部开始的,第一次读取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文件占用的存储空间也更小,这点在前面的测试比照中也有所印证。
文件压缩
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+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字段外面呈现,他们是:

三、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文件,属于非凡需要场景。