Parquet 与 ORC:高性能列式存储 | 青训营笔记

241 阅读3分钟

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

1. 列存 vs 行存

1.1. 数据格式层概述

  • 计算层:各种计算引擎
  • 存储层:承载数据的持久化存储
  • 数据格式层:定义了存储文件内部的组织格式,计算引擎通过格式层的支持来读写文件

image.png

1.2. 分层视角下的数据形态

  • 存储层:File,Blocks
  • 格式层:File内部的数据布局(Layout + Schema)
  • 计算引擎:Rows + Columns

image.png

1.3. 两种数据查询分析场景:OLTPP vs OLAP

image.png

1.4. OLTP:行式存储

  • 每行的数据在文件上是连续存储的
  • 读取整行数据效率高,单词IO顺序读即可
  • 典型系统:
    • 关系型数据库:MySQL、Oracle
    • Key-Value数据库

image.png

1.5. OLAP:列式存储

  • 每列的数据在文件上是连续存储的
  • 读取整列的效率较高
  • 同列的数据类型一致,压缩编码的效率更好
  • 典型系统:
    • 大数据分析系统:SQL-on-Hadoop,数据湖分析
    • 数据仓库:ClickHouse,Greenplum,阿里云MaxCompute

image.png

2. Parquet 原理详解

2.1. Parquet 简介

image.png

2.1.1. Parquet in Action - DDL

image.png

2.1.2. Parquet in Action - Spark

image.png

2.1.3. Parquet in Action - Spark

image.png

2.1.4. Parquet in Action - Parquet vs Text Format

image.png

2.1.5. Partquet in Action - Spark

image.png

2.2. Dremel 数据模型

image.png

2.2.1. Dremel 数据模型 - Continued

image.png

2.3. 数据布局

image.png

2.4.编码 Encoding

  • Plain 直接存储原始数据
  • Run Length Encoding (RLE):适用于列基数不大,重复值较多的场景,例如:Boolean、枚举、固定的选项等等
    • Bit-Pack Encoding:配合RLE编码使用,让整形数字存储的更加紧凑
  • 字典编码Dictionary Encoding:适用于列基数不大的场景,构造字典表,写入到Dictionary Page;把数据用字典Index替换,然后用RLE编码

image.png

2.4.1. 编码Encoding

image.png

2.5. 压缩 Compression

image.png

2.6. 索引 Index

  • 和传统的数据库相比,索引支持非常简陋
  • Min-Max Index:记录Page内部Column的min_value和max_value
  • column Index:
    • Footer 里的Column Metadata包含ColumnChunk的全部Page的Min-Max Value
  • Offset Index:记录Page在文件中的Offset和Page的Row Range

image.png

2.6.1. Index -Bloom Filter

image.png

2.6.2. 排序 Ordering

  • 类似于聚集索引的概念
  • 排序帮助更好的过滤掉无关的RowGroup或者Page
    • 对于少量数据Seek很有帮助
  • Parquet Format 支持SortingColumns
  • Parquet Library目前没有支持
  • 依赖业务侧根据查询特征去保证顺序

2.7. 过滤下推 Predicate PushDown

image.png

2.8. Spark集成 - 向量化读

image.png

2.9. 深入Dremel 数据模型 - Repetition Level

image.png

2.9.2. 深入Dremel 数据模型 - Re-Assembly

image.png

3. ORC 详解和对比

3.1. ORC 简介

image.png

3.2. 数据模型

  • OCR 会给包括根节点在内的中间节点都创建一个Column
  • 嵌套类型或者集合类型支持和Parquet差别较大
  • optional和repeated字段依赖父节点记录额信息来重新Assembly数据

image.png

3.3. 数据布局

image.png

3.4. ACID 特性简介

  • 支持Hive Transactions 实现,目前只有Hive本身集成
  • 类似Delta Lake/Hudi/Iceberg
  • 基于Base + Delta + compaction的设计

image.png

3.5. AliORC

image.png

3.5.1. AliORC - 索引增强

image.png

3.5.2. AliORC - 小列聚合

image.png

3.5.3. AliORC - 异步预取

image.png

3.6. Parquet vs ORC对比

  • 从原理层面,最大的差别就是对于NestedType和复杂类型处理上
  • Parquet的算法上要复杂很多,带来的CPU的开销比ORC要略大
  • ORC的算法上相对简单,但是要读取更多的数据
  • 因此,这个差异的对业务效果的影响,很难做一个定性的判定,更多的时候还是要取决于实际的业务场景

3.6.1. Parquet vs ORC 对比 - 性能

image.png

image.png

3.6.2. Parquet vs ORC 对比 - 选择

  • 最新的版本来看,Parquet和ORC在性能上没有非常明显的差距和短板
  • 性能上很多情况下依赖于数据集和测试环境,不能迷信Benchmark的结果
  • 根据实际业务做充分的测试调优
  • Spark生态下Parquet比较普遍
  • Hive生态下ORC有原生支持

整体上,Spark比Hive更加有优势,所以大部分情况下,Parquet可能是个更好的选择

4. 列存演进

4.1. 数仓中的列存

image.png

4.2. 存储侧下推

image.png

4.3. Column Family支持

image.png