ORC源码阅读(2)
来源:互联网 发布:重装系统 软件 编辑:程序博客网 时间:2024/05/18 03:02
读了mapreduce module之后,对orc的代码有了大概的了解,其实顺着RecordReader、Write和OrcFile、OrcStruct往下读就会有很多和protocol buffers有关的东西。其实如果对orc没有基本了解的话,应该先结合orc的文档读一下proto文件,这个文件在orc源码的proto目录下,文件名为orc_proto.proto。
orc使用protocol buffers来定义一个orc file中元数据的结构。在编译orc源码的时候,会根据proto文件中的message定义生成相应的类,以及类序列化、反序列化的java代码。生成的代码默认在java/core/target/generated_sources/org/apache/orc/OrcProto.java中。但是在实际读写orc file的时候,序列化和反序列化并不完全依赖与生成的代码,比如写FileTail的时候,按照proto中的定义,应该是先写postscript再写footer,而实际上在orc的org.apache.orc.impl.WriterImpl中,是先写footer再写postscript的。
Documentation
首先看一下文档中的文件格式相关的重点:
Type
https://orc.apache.org/docs/types.html
All ORC file are logically sequences of identically typed objects. Hive always uses a struct with a field for each of the top-level columns as the root object type, but that is not required. All types in ORC can take null values including the compound types.
Index
https://orc.apache.org/docs/indexes.html
https://orc.apache.org/docs/spec-index.html
这两个全程重点
Introduction
https://orc.apache.org/docs/spec-intro.html
全程重点
FileTail
https://orc.apache.org/docs/file-tail.html
全程重点
Compression
https://orc.apache.org/docs/compression.html
随便看看
Stripe
https://orc.apache.org/docs/stripes.html
全程重点
ColumnEncoding
https://orc.apache.org/docs/encodings.html
全程重点,这个对理解proto定义很关键
https://orc.apache.org/docs/run-length.html
随便看看,介绍了数值型的列怎么做RLE。
然后开始读proto文件。
这个文件可以从前往后大概读一遍,然后从后往前细细的读。我们就从后往前读。
FileTail
FileTail是一个Orc文件的尾部。对应于Introduction的图中文件结构最后的那一小块。定义如下:
message FileTail { optional PostScript postscript = 1; optional Footer footer = 2; optional uint64 fileLength = 3; optional uint64 postscriptLength = 4;}
这个FileTail的定义其实是有问题的,和文档不一致(https://orc.apache.org/docs/spec-intro.html、https://orc.apache.org/docs/file-tail.html),而文档中的部分内容又和以及实际的orc file中的内容不一致。感觉这是有意的,不知道为啥要这样,可能就是因为懒得弄或者作者的某种癖好吧。一切以实际的orc file中写入的内容为准,在WriterImpl中可以看到:
private long writeFooter() throws IOException { writeMetadata(); OrcProto.Footer.Builder builder = OrcProto.Footer.newBuilder(); builder.setNumberOfRows(rowCount); builder.setRowIndexStride(rowIndexStride); rawDataSize = computeRawDataSize(); // serialize the types writeTypes(builder, schema); // add the stripe information for(OrcProto.StripeInformation stripe: stripes) { builder.addStripes(stripe); } // add the column statistics writeFileStatistics(builder, treeWriter); // add all of the user metadata for(Map.Entry<String, ByteString> entry: userMetadata.entrySet()) { builder.addMetadata(OrcProto.UserMetadataItem.newBuilder() .setName(entry.getKey()).setValue(entry.getValue())); } builder.setWriter(OrcFile.WriterImplementation.ORC_JAVA.getId()); physicalWriter.writeFileFooter(builder); return writePostScript(); }
在写文件的footer的writeFooter方法中,最开始就写入了metadata,最后写入了postscript。
可见:
1. FileTail里面实际上是有Metadata的;
2. 虽然postscript的id是1,但是实际上在序列化的时候,会先序列化footer再序列化postscript
实际上,在读取一个orc file的时候,会先从文件的末尾读取16KB,并从最后一个字节中取出postscript的长度(虽然postscriptLength是64位,但是实际上postscript长度不超过255个字节,所以只用了一个字节)。
PostScript
定义如下:
message PostScript { optional uint64 footerLength = 1; optional CompressionKind compression = 2; optional uint64 compressionBlockSize = 3; repeated uint32 version = 4 [packed = true]; optional uint64 metadataLength = 5; optional uint32 writerVersion = 6; optional string magic = 8000;}
从postscript中就知道footer的长度、压缩算法、元数据长度,然后就可以计算出footer和metadata所在的偏移。
File Footer
定义如下:
message Footer { optional uint64 headerLength = 1; optional uint64 contentLength = 2; repeated StripeInformation stripes = 3; repeated Type types = 4; repeated UserMetadataItem metadata = 5; optional uint64 numberOfRows = 6; repeated ColumnStatistics statistics = 7; optional uint32 rowIndexStride = 8; // Each implementation that writes ORC files should register for a code // 0 = ORC Java // 1 = ORC C++ // 2 = Presto optional uint32 writer = 9;}
headerLength永远是3,orc file会在文件开头存’ORC’3个字符,占用3个字节,这个叫做header,用来告诉其他不明所以的file reader这是个ORC文件,不要乱读。其他字段的解释看文档https://orc.apache.org/docs/file-tail.html.
其中stripes就是各个stripe的元数据,types是文件的schema。rowIndexStride是个配置参数,指一个index entry中最大的行数。这个index entry就是指proto中的RowIndexEntry,一个stripe中有很多column,每个column上的每10000行为一个row group,一个row group上面有一个RowIndexEntry。
stripes就是存了各个stripe的偏移和stripe中index、data、footer等部分的length。定义如下:
message StripeInformation { optional uint64 offset = 1; optional uint64 indexLength = 2; optional uint64 dataLength = 3; optional uint64 footerLength = 4; optional uint64 numberOfRows = 5;}
types是个很有意思的结构,目测(我也不是非常确定)types对应于https://orc.apache.org/docs/types.html 最后那个图里面的那棵树上的各个节点,节点上的id就是在types数组中的下标。可以看一下Type的定义:
message Type { enum Kind { BOOLEAN = 0; BYTE = 1; SHORT = 2; INT = 3; LONG = 4; FLOAT = 5; DOUBLE = 6; STRING = 7; BINARY = 8; TIMESTAMP = 9; LIST = 10; MAP = 11; STRUCT = 12; UNION = 13; DECIMAL = 14; DATE = 15; VARCHAR = 16; CHAR = 17; } optional Kind kind = 1; repeated uint32 subtypes = 2 [packed=true]; repeated string fieldNames = 3; optional uint32 maximumLength = 4; optional uint32 precision = 5; optional uint32 scale = 6;}
其中的subtypes和fieldNames应该分别是该节点的孩子节点在types的下标和名称。如果是这样,那么types就是一颗以数组形式存储的树,这样存确实非常方便也比较省空间。
Metadata
Metadata分为UserMetadata和Metadata。UserMetadata是作为footer一部分存储的,在上面的footer定义中可以看到。定义如下:
message UserMetadataItem { optional string name = 1; optional bytes value = 2;}
就是一个key-value的形式,没啥好说的,用来让用户自定义些什么东西放在footer里面。
Metadata的定义如下:
message Metadata { repeated StripeStatistics stripeStats = 1;}message StripeStatistics { repeated ColumnStatistics colStats = 1;}message ColumnStatistics { optional uint64 numberOfValues = 1; optional IntegerStatistics intStatistics = 2; optional DoubleStatistics doubleStatistics = 3; optional StringStatistics stringStatistics = 4; optional BucketStatistics bucketStatistics = 5; optional DecimalStatistics decimalStatistics = 6; optional DateStatistics dateStatistics = 7; optional BinaryStatistics binaryStatistics = 8; optional TimestampStatistics timestampStatistics = 9; optional bool hasNull = 10;}message IntegerStatistics { optional sint64 minimum = 1; optional sint64 maximum = 2; optional sint64 sum = 3;}message DoubleStatistics { optional double minimum = 1; optional double maximum = 2; optional double sum = 3;}message StringStatistics { optional string minimum = 1; optional string maximum = 2; // sum will store the total length of all strings in a stripe optional sint64 sum = 3;}message BucketStatistics { repeated uint64 count = 1 [packed=true];}message DecimalStatistics { optional string minimum = 1; optional string maximum = 2; optional string sum = 3;}message DateStatistics { // min,max values saved as days since epoch optional sint32 minimum = 1; optional sint32 maximum = 2;}message TimestampStatistics { // min,max values saved as milliseconds since epoch optional sint64 minimum = 1; optional sint64 maximum = 2; optional sint64 minimumUtc = 3; optional sint64 maximumUtc = 4;}message BinaryStatistics { // sum will store the total binary blob length in a stripe optional sint64 sum = 1;}
虽然长了点,但是很清晰,就是存了一个orc file中各个stripe中的各个column上的统计信息,如最大值、最小值等等。
FileTail这样就算是基本捋清楚了。
Stripe
一个Orc file除了末尾的file tail和开头的三字节header这两部分,就是file tail和header之间的各个stripe。
如https://orc.apache.org/docs/spec-intro.html 所述,每个stripe有index、data、footer三个部分。注意这里的footer是stripe footer,不是file footer。
Stripe Footer
首先看footer定义如下:
message StripeFooter { repeated Stream streams = 1; repeated ColumnEncoding columns = 2; optional string writerTimezone = 3;}message Stream { // if you add new index stream kinds, you need to make sure to update // StreamName to ensure it is added to the stripe in the right area enum Kind { PRESENT = 0; DATA = 1; LENGTH = 2; DICTIONARY_DATA = 3; DICTIONARY_COUNT = 4; SECONDARY = 5; ROW_INDEX = 6; BLOOM_FILTER = 7; BLOOM_FILTER_UTF8 = 8; } optional Kind kind = 1; optional uint32 column = 2; optional uint64 length = 3;}message ColumnEncoding { enum Kind { DIRECT = 0; DICTIONARY = 1; DIRECT_V2 = 2; DICTIONARY_V2 = 3; } optional Kind kind = 1; optional uint32 dictionarySize = 2; // The encoding of the bloom filters for this column: // 0 or missing = none or original // 1 = ORC-135 (utc for timestamps) optional uint32 bloomEncoding = 3;}
这个有点复杂,一个footer中有若干个stream和若干个column encoding,参见https://orc.apache.org/docs/encodings.html,这里面讲了stream和column encoding到底是啥关系。这块太复杂了,后面慢慢补充。
Index
Stripe的data部分就不用定义了,反正就是二进制数据。Index部分的定义如下:
message RowIndexEntry { repeated uint64 positions = 1 [packed=true]; optional ColumnStatistics statistics = 2;}message RowIndex { repeated RowIndexEntry entry = 1;}message BloomFilter { optional uint32 numHashFunctions = 1; repeated fixed64 bitset = 2; optional bytes utf8bitset = 3;}message BloomFilterIndex { repeated BloomFilter bloomFilter = 1;}
详细的参见:
https://orc.apache.org/docs/indexes.html
https://orc.apache.org/docs/spec-index.html
- ORC源码阅读(2)
- ORC源码阅读(1)
- orc
- presto源码分析(hive orc读取)
- ORC 2V2 详细心得
- OpenCV源码阅读(2)
- struts2源码阅读2
- Hadoop源码阅读2
- tomcat源码阅读-2
- 源码阅读 2 ErrorView
- LevelDB源码阅读(2)
- Struts2 源码阅读(2)_扩展阅读
- Struts2 源码阅读(2)_扩展阅读
- x264源码阅读笔记2
- java String源码阅读2
- struts1源码阅读(2)
- Tomcat7源码阅读日记2
- Hadoop RPC 源码阅读2
- SpringData的@Query的注释操作
- ecipse下maven的配置
- 浅谈JDK8相对于JDK7的一些新特性
- QT-单例类
- IntelliJ IDEA 2017.2+ JRebel 7.0.12 热部署
- ORC源码阅读(2)
- 微信支付-商户后台(4)
- 动态实现简单【顺序表】
- unity动作的游戏开发笔记2
- centos764 gitlab 9.3.5版本安装及汉化
- OpenCV3.2+Qt5.8.0+Win10环境配置
- 零基础APP自动化测试教程(二)
- WUST OJ 1956:回文数(思维)
- 基于ThinkPHP3.2.3的微信OAuth2.0微信网页授权 微信公众号网页登录 改装