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

149 阅读7分钟

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


今日学习内容:

1.列式存储和行式存储

2.Parquet 原理详解

3.ORC 原理详解及与Parquet 的对比

4.列存演进

01 列式存储和行式存储

1.1 数据格式及数据形态

image.png

计算层:各种计算引擎(Rows + Columns)

存储层:承载数据的持久化存储(File,Blocks)

数据格式层:定义了存储层文件内部的组织格式,计算引擎通过格式层的支持来读写文件。(File内部的数据布局:Layout + Schema)

image.png

1.2 OLTP vs OLAP

 OLTPOLAP
典型场景在线业务系统,例如:订单、交易、社交、评论等数据仓库或者大数据分析系统,例如:决策分析、BI系统、推荐系统等
访问特征事务、实时性、低延时、高并发、高可用弱事务性、近实时、离线分析、大吞吐、并发相对不高、可用性(可以有一定的妥协)
数据模型特征Schema 相对简单、数据维度不多、数据规模较小Schema 复杂、数据维度很多(几百个Column 很常见)、数据规模巨大

1.2.1 OLTP:行式存储格式 (行存)

image.png

  • 每一行 (Row) 的数据在文件的数据空间里连续存储的;
  • 读取整行的效率比较高,一次顺序 IO 即可;
  • 在典型的 OLTP 型的分析和存储系统中应用广泛,例如:MySQL、Oracle、RocksDB、key-Value数据库等

1.2.2 OLAP:列式存储格式 (列存)

image.png

  • 每一列 (Column) 的数据在文件的数据空间里连续存放的
  • 同列的数据类型一致,压缩编码的效率更好
  • 在典型的 OLAP 型分析和存储系统中广泛应用,例如:
  • 大数据分析系统:Hive、Spark,数据湖分析
  • 数据仓库:ClickHouse,Greenplum,阿里云 MaxCompute

02 Parquet 原理详解

Parquet:大数据分析领域使用最广的列存格式(Spark推荐存储格式)

2.1 Parquet in Action

  • Spark生成的文件会有.parquet后缀,Hive生成的文件没有后缀
  • Parquet 压缩文件性能较好
  • parquet-cli工具查看parquet文件的具体信息

2.2 Dremel数据模型

image.png

  • Protocol Buffer 定义
  • 支持可选和重复字段
  • 支持嵌套类型

image.png image.png

 嵌套类型只保存叶子节点数据

2.2.1 Repetition Level

  Repetition Level:该字段在 Field Path 上第几个重复字段上出现

  • 0:标识新的Record
  • Name.Language.Code为例,Name是第1个重复字段,Language是第2个重复字段

image.png image.png

2.2.2Definition Level

Definition Level:用来记录在fieldpath中,有多少个字段是可以不存在(optional/repeated)而实际出现的

  • Name.Language.Code为例,Name和Language都是可以不存在的
  • 第一个NULL字段,D是1,说明Name是存在的,但是 Language是不存在的,保留原有的信息

2.2.3Re-Assembly

image.png image.png

  • 根据全部或者部分列数据,重新构造 Record
  • 构造FSM状态机
  • 根据同一个 Col u mn下一个记录的RepetionLevel决定继续读的列

2.3 数据布局

image.png

(1) RowGroup: 每一个行组包含一定数量或者固定大小的行的集合,在 HDFS 上,RowGroup 大小建议配置成 HDFS Block大小

(2) ColumnChunk: RowGroup 中按照列切分成多个 ColumnChunk

(3) Page:ColumnChunk内部继续切分成 Page,一般建议 8KB 大小。Page 是压缩和编码的基本单元(根据保存的数据类型,Page 可以分为:Data Page,Dictionary Page,Index Page)

(4) Footer 保存文件的元信息

  • Schema

  • Config

  • Metadata

    • RowGroup Meta

      • Column Meta

2.4 编码Encoding

image.png

image.png

(1) Plain 直接存储原始数据

(2) Run Length Encoding(RLE):适用于列基数不大,重复值较多的场景,例如: Boolean、枚举、固定的选项等。( Bit-Pack Encoding: 配合RLE编码使用,让整形数字存储的更加紧凑)

(3) 字典编码Dictionary Encoding:适用于列基数不大的场景,构造字典表,写入到Dictionary Page;把数据用字典Index替换,然后用RLE编码

编码的选择:

  • 默认场景下parquet-mr 会自动根据数据特征选择
  • 业务自定义: org.apache.parquet.column.values.factory.ValuesWriterFactory

2.5 压缩Compression

(1) Page完成 Encoding 以后,进行压缩

(2) 支持多种压缩算法

(3) snappy:压缩速度快,压缩比不高,适用于热数据

(4) gzip:压缩速度慢,压缩比高,适用于冷数据

(5) zstd:新引入的压缩算法,压缩比和gzip差不多,而且压缩速度比肩Snappy

(6) 建议选择snappy或者zstd,根据业务数据类型充分测试压缩效果,以及对查询性能的影响

2.6 索引Index

image.png 和传统的数据库相比,索引支持非常简陋

  • Min-Max Index:记录 Page 内部Column的min_value和max_value
  • Column Index:Footer 里的Column Metadata包含ColumnChunk的全部 Page 的 Min-MaxValue
  • Offset lIndex:记录 Page在文件中的Offset和Page的 Row Range

Bloom Filter

image.png parquet.bloom.filter.enabled:对于列基数比较大的场景,或者非排序列的过滤,Min-Max Index很难发挥作用,引入 Bloom Filter 加速过滤匹配判定,每个ColumnChunk的头部保存Bloom Filter数据,Footer记录 Bloom Filter的page offset。

排序

(1) 类似于聚集索引的概念

(2) 排序帮助更好的过滤掉无关的 RowGroup或者Page,对于少量数据Seek很有帮助

(3) Parquet Format支持 SortingColumns

(4) Parquet Library目前没有支持

(5) 依赖业务侧根据查询特征去保证顺序

2.7 过滤下推 Predicate PushDown

image.png

(1) parquet-mr 库实现,实现高效的过滤机制

(2) 引擎侧传入Filter Expression

(3) parquet-mr转换成具体Column的条件匹配

(4) 查询Footer 里的 Column Index,定位到具体的行号

(5) 返回有效的数据给引擎侧

2.8 Spark 集成:向量化读

(1) ParquetFileFormat类

(2) 向量化读开关:spark.sql.parquet.enableVectorizedReader

(3) 向量化读是主流大数据分析引擎的标准实践,可以极大的提升查询性能

(4) Spark以 Batch的方式从 Parquet 读取数据,下推的逻辑也会适配 Batch的方式

03 ORC 原理详解及与Parquet 的对比

ORC:大数据分析领域使用最广的列存格式之一,出自于Hive项目。

3.1 数据模型

image.png image.png

(1) ORC会给包括根节点在内的中间节点都创建一个Column(左图中,创建8个Column)

(2) 嵌套类型或者集合类型支持和Parquet差别较大

(3) optional和repeated字段依赖父节点记录额信息来重新Assembly 数据

3.2 数据布局

image.png

  • 类似 Parquet
  • Rooter + Stripe + Column + Page (Row Group)结构
  • Encoding / Compression / lndex支持上和Parquet几乎—致

3.3 ACID特性

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

3.4 AliORC

ORC在阿里云计算平台被广泛应用,主流产品MaxCompute +交互式分析Hologres的最新版本都支持ORC格式,AliORC是对ORC的深度定制版。

3.4.1 索引增强

image.png image.png

支持Clusterd lndex,更快的主键查主

支持Bitmap Index,更快的过滤(Roaring Bitmap)

3.4.2 小列聚省

image.png

小列聚合,减少小lO(重排Chunk)

3.4.3 异步预取

image.png

  • 异步预取数据
  • 计算逻辑和数据读取并行化

3.5 Parquet vs.ORC 对比

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

在Spark场景下Parquet工作的更好;在Hive场景下,ORC更好

Parquet vs.ORC的选择:

(1) 最新的版本来看,Parquet 和 ORC在性能上没有非常明显的差距和短板

(2) 性能上很多情况下依赖于数据集和测试环境,不能迷信Benchmark 结果

(3) 根据实际业务做充分的测试调优

(4) Spark生态下Parquet比较普遍

(5) Hive生态下ORC有原生支持

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

04 列存演进

4.1 数仓中的列存

image.png

  • ClickHouse的MergeTree引擎也是基于列存构建的
  • 默认情况下列按照Column拆分的
  • 支持更加丰富的索引
  • 湖仓一体的大趋势

4.2 存储侧下推

image.png

  • 更多的下推工作下沉到存储服务侧
  • 越接近数据,下推过滤的效率越高
  • 例如Aws s3 Select功能
  • 挑战:存储侧感知Schema,计算生态的兼容和集成

4.3 Column Family支持

image.png

背景:Hudi数据湖场景下,支持部分列的快速更新

  • 在Parquet格式里引入 Column Family概念,把需要更新的列拆成独立的Column Family
  • 深度改造Hudi的 Update和Query逻辑,根据Column Family选择覆盖对应的Column Family
  • Update操作实际效果有10+倍的提升