Parquet | 青训营笔记

77 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第7天

Parquet 列式存储格式 面向分析型业务的列式存储格式 由 Twitter 和 Cloudera 合作开发,2015 年 5 月从 Apache 的孵化器里毕业成为 Apache 顶级项目

列式存储

列式存储和行式存储相比有哪些优势呢? 可以跳过不符合条件的数据,只读取需要的数据,降低 IO 数据量。 压缩编码可以降低磁盘存储空间。由于同一列的数据类型是一样的,可以使用更高效的压缩编码(例如 Run Length Encoding 和 Delta Encoding)进一步节约存储空间。 只读取需要的列,支持向量运算,能够获取更好的扫描性能。 当时 Twitter 的日增数据量达到压缩之后的 100TB+,存储在 HDFS 上,工程师会使用多种计算框架(例如 MapReduce, Hive, Pig 等)对这些数据做分析和挖掘; 日志结构是复杂的嵌套数据类型,例如一个典型的日志的 schema 有 87 列,嵌套了 7 层。所以需要设计一种列式存储格式,既能支持关系型数据(简单数据类型),又能支持复杂的嵌套类型的数据,同时能够适配多种数据处理框架。

关系型数据的列式存储,可以将每一列的值直接排列下来,不用引入其他的概念,也不会丢失数据。

关系型数据的列式存储比较好理解,而嵌套类型数据的列存储则会遇到一些麻烦。 如图 1 所示,我们把嵌套数据类型的一行叫做一个记录(record),嵌套数据类型的特点是一个 record 中的 column 除了可以是 Int, Long, String 这样的原语(primitive)类型以外,还可以是 List, Map, Set 这样的复杂类型。 在行式存储中一行的多列是连续的写在一起的,在列式存储中数据按列分开存储,例如可以只读取 A.B.C 这一列的数据而不去读 A.E 和 A.B.D,那么如何根据读取出来的各个列的数据重构出一行记录呢?

Google 的Dremel系统解决了这个问题,核心思想是使用“record shredding and assembly algorithm”来表示复杂的嵌套数据类型,同时辅以按列的高效压缩和编码技术,实现降低存储空间,提高 IO 效率,降低上层应用延迟。

Parquet 就是基于 Dremel 的数据模型和算法实现的。 Parquet 适配多种计算框架 Parquet 是语言无关的,

而且不与任何一种数据处理框架绑定在一起,

适配多种语言和组件,能够与 Parquet 配合的组件有:

查询引擎: Hive, Impala, Pig, Presto, Drill, Tajo, HAWQ, IBM Big SQL 计算框架: MapReduce, Spark, Cascading, Crunch, Scalding, Kite 数据模型: Avro, Thrift, Protocol Buffers, POJOs

那么 Parquet 是如何与这些组件协作的呢?

数据从内存到 Parquet 文件或者反过来的过程主要由以下三个部分组成:

存储格式 (storage format) parquet-format项目定义了 Parquet 内部的数据类型、存储格式等。

对象模型转换器 (object model converters) 这部分功能由parquet-mr项目来实现,主要完成外部对象模型与 Parquet 内部数据类型的映射。

对象模型 (object models) 对象模型可以简单理解为内存中的数据表示,Avro, Thrift, Protocol Buffers, Hive SerDe, Pig Tuple, Spark SQL InternalRow 等这些都是对象模型。Parquet 也提供了一个example object model 帮助大家理解。

例如parquet-mr项目里的 parquet-pig 项目就是负责把内存中的 Pig Tuple 序列化并按列存储成 Parquet 格式,以及反过来把 Parquet 文件的数据反序列化成 Pig Tuple。