文章目录
概述
本文基于上一篇文章 Hive存储格式之RCFile详解,RCFile的过去现在未来 撰写,读过上一篇文章,则更好理解以下内容。
2013年,HortonWorks在RCFile的基础上开发出了ORC File(Optimied Row Columnar),在2015年成为Apache的顶级项目。以下简称ORC。
RCFile在被Facebook开源后,作为Hive之中典型的列存储模型被广泛使用,相比于之前的存储格式有很大的优势,但是同样RCFile仍然有值得改进的地方。
ORC 做了相关优化,在Hive的使用中有更好的表现,它支持复杂数据类型、ACID支持及内置索引支持,非常适合海量数据的存储。
ORC并不是一个单纯的列式存储格式,它也遵循了先水平分区,再垂直分区的理念,采用混合存储结构。
除了Hive,目前也被Spark SQL,Flink,Presto,Impala等查询引擎支持。
我上一篇中提及RCFile的两个优化方向:
- 不同数据类型的列使用不同的压缩方案(Facebook论文指出的优化方向-未做)
- 全局检索性能查,提供更合理快速的检索功能
ORC相对于RCFile提供了更优的解决方案:
- 列数据的类型感知:与RCFile之前未对列数据都统一为BLOB(binary large object-二进制大对象)数据不同,ORC可以感知列的数据类型,做出更为合理的数据压缩选择。
- 嵌套数据类型支持:ORC可以在列数据之中插入Struct,Union,List,Map等数据,让数据操作更加灵活,也更加适合非结构化数据的存储与处理。
- 谓词下推:这个算是RCFile原先功能的补强,在元数据层面增加了很多内容,来利用谓词下推加速处理的过程。ORC自己称之为轻量级索引,其实就是一些相较于RCFile更为详细的统计数据。
- 文件可切分:文件可切分,在Hive中使用ORC作为表的文件存储格式,不仅可以节省HDFS的存储资源,查询任务的输入数据量减少,使用的MapTask也就减少了。
- 内存管理:提供了一个memory manager来管理内存使用情况。
接下来我们通过以下几部分来完整的理解一下什么是ORC。
文件存储结构
ORC文件是以二进制的方式存储的,不可以直接读取,但由于ORC的自描述特性,其读写不依赖于 Hive Metastore 或任何其他外部元数据。本身存储了文件数据、数据类型及编码信息。因为文件是自包含的,所以读取ORC文件数据无需考虑用户使用环境。
由于ORC的元数据使用Protocol Buffers序列化,添加新字段不会破坏原有的数据结构。
如下图所示,ORC引入了三个新的组件。
-
Stripe
-
File Footer
-
PostScript
Stripe
ORC的主体由多个Stripe(也成为条带)组成,类似于RCFile中的行组,但是其远远大于行组的4MB,最大可达到250M大小,更大的Stripe使ORC的数据读取更加高效。
每个Stripe彼此独立,这个很好理解,因为每行数据彼此独立,而每行数据不会在多个Stripe中。
在Hive中每个Stripe通常由不同的任务处理。列存储格式的定义特征是每一列的数据是分开存储的,从文件中读取数据的速度应该与读取的列数成正比。
Stripe又包含三个部分:Index Data、Row Data和Stripe Footer。索引和数据部分都按列划分,因此只需要读取所需列的数据。
Index Data
索引数据部分,存储每列的统计数据。Index Data在Stripe的最前面,因为它们只在使用谓词下推或寻找指定行时加载。(这里主要利用索引功能实现的,具体见下文条带级别索引)
Row Data
实际存储数据的单元,利用列存储原理,对不同列可以实现不同的压缩方案,所有的列数据可以组成行数据。
Stripe Footer
存储了每个列的编码,数据流目录与位置。
message StripeFooter {
// the location of each stream
repeated Stream streams = 1;
// the encoding of each column
repeated ColumnEncoding columns = 2;
optional string writerTimezone = 3;
// one for each column encryption variant
repeated StripeEncryptionVariant encryption = 4;
}
两个补充名词
在数据存储和解析的过程中还使用到了两个比较抽象的名词描述,分别为Row Group和Stream,这里单独说明一下。
Row Group
这里的Row Group和RCFile里的行组不是同一个概念,RCFile的行组对标的是ORC中的Stripe。
Row Group是虚拟的(下文有详细介绍),Row Group Index是索引(index)的最小单位,一个Index Data中包含多个行组。默认值为 10000 个值。每一个Row Group Index中有多少条记录在文件的Footer中存储。
Stream
本节以上部分是Stripe的逻辑结构,具体数据存储还有更细粒度的单位存在,那就是Stream。在ORC文件中,每一列都存储在多个Stream中,这些Stream在文件中彼此相邻存储。Stream保存了用户真正关心的业务数据内容。
这也是ORC列式存储的根本所在:正如开头的架构图一样,一个大文件由各Stripe分割,每个Stripe负责多个行组,在一个Stripe负责的这多行范围内,各列的数据内容以Stream的形式按列存储。为了描述每个Stream,ORC以字节为单位存储Stream的类型、列ID和Stream的大小。每个Stream中存储内容的详细信息取决于列的类型和编码。也就是说,在一个Stripe中的每一列都可能有多个表示不同信息的Stream,存储内容如下所示:
message Stream {
enum Kind {
// boolean stream of whether the next value is non-null
PRESENT = 0;
// the primary data stream
DATA = 1;
// the length