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

118 阅读5分钟

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

1. 列存 vs 行村

1.1 数据格式层概述

计算层:各种计算引擎

数据格式层:定义了存储层文件内部的组织形式

存储层:承载数据的持久化存储

计算引擎通过格式层的支持来读写文件

image.png

1.2 分层视角下的数据形态

计算层:File、Blocks

格式层:File内部的数据布局layout + Schema

计算引擎:Rows + Columns

image.png

1.3 两种数据查询分析场景

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

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

每行的数据在文件上是连续存储的

读取整行数据效率高、单词IO顺序读即可

典型系统:

  • 关系型数据库
  • Key-Value数据库

image.png

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

每列的数据在文件上是连续存储的

读取整列的效率较高

同列的数据类型一致,压缩编码的效果更好

典型系统:

  • 大数据分析系统:SQL on Hadoop、数据湖分析
  • 数据仓库:ClickHouse、Greeplum、阿里云MaxCompute

image.png

1.6 总结

  1. 格式层定义了数据的布局,连接计算引擎和存储服务;
  2. OLTP和OLAP场景差异明显
  3. 业务场景决定了技术实现,行存适用于OLTP,列存适用于OLAP

2 Parquet原理

2.1 简介

大数据分析领域使用最广泛的列存格式,Spark推荐的列存格式

Hive Table using Parquet:

CREATE TABLE lineitem(
......
)
STORED AS PARQUET TBLPROPERTIES("parquet.compression"="SNAPPY");

Load Data using SparkSQL:

INSERT INTO lineitem SELECT * from tpch10g.lineitem;
  • Spark生成的文件会有.parquet后缀
  • Hive生成的文件没有后缀

parquet相比于Text,可以极大的压缩文件空间

2.2 Dremel数据模型

  • Protocal Buffer定义
  • 支持可选和重复字段
  • 支持嵌套格式

image.png

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

image.png

2.3 数据布局

RowGroup: 每一行组包含一定数量或者固定大小的行集合

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

Page: ColumnChunk内部继续切分成Page,一般建议8kB

Footer: 保存文件的元信息

image.png

2.4 编码

Plain直接存储原始数据

Run Length Encoding(RLE): 适用于列基数不大,重复值较多的场景

Bit-Pack Encoding: 配合RLE编码使用,让整形数字存储的更加紧凑

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

image.png

2.5 压缩

Page完成Encoding以后,进行压缩

  • snappy: 压缩速度快,压缩比不高,适用于热数据
  • gzip: 压缩速度慢,压缩比高,适用于冷数据
  • zstd: 新引入的压缩算法,压缩比和gzip相当,压缩速度和snappy相当

image.png

2.6 索引

和传统数据库相比,parquet支持的索引相当简陋

  • 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

布隆过滤器:给三个Hash算法,对每个值进行哈希运算,得到三个位置。当再来一个新值时继续算出三个位置,看存不存在,是一种近似算法。

2.7 过滤下推Predicate PushDown

image.png

2.8 Spark集成——向量化读

向量化读是主流大数据分析引擎的标准实践,可以极大提升查询效率

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

2.9 parquet总结

  • 数据模型:基于Dremel
  • 文件布局:Footer+RowGroup+ColumnChunk+Page
  • Encoding:Page粒度,Plain/RLE/Dictionary
  • Compression:Snappy/Gzip/Zstd
  • Index:Column Index
  • Predicate PushDown

3. ORC

3.1 简介

大数据分析领域使用最广的列存格式之一

出自Hive项目

CREATE TABLE table_name(
......
)
STORED AS ORC;

3.2 数据模型

ORC会给包括根节点在内的中间节点都创建一个column

嵌套类型或者集合类型支持和parquet差别大

optional和repeated字段依赖父节点记录额外信息来重新恢复数据

image.png

3.3 数据布局

类似parquet

Rooter+Stripe+column+Page结构

编码/压缩/索引支持和parquet几乎一致

3.4 ACID特性

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

3.5 parquet 和 ORC 对比

  • parquet算法复杂,带来CPU开销比ORC略大
  • ORC算法相对简单,但要读取更多数据
  • Spark生态下parquet比较普遍
  • Hive生态下ORC有原生支持

4 列存演进

4.1 数仓中的列存

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

4.2 存储侧下推

  • 更多的下推工作下沉到存储服务侧
  • 越接近数据,下推过滤的效率越高
  • 例如:AWS S3 的 select共呢个

4.3 Column Family支持

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

  • 在parquet中引入column family概念,把需要更新的列拆成独立的Column Family