Parquet 笔记 | 青训营笔记

542 阅读4分钟

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

Parquet 原理详解

1、Parquet 简介

image.png

  • parquet.apache.org
  • 大数据分析领域使用最广的列存格式
  • Spark 推荐存储格式
  • Github
    • parquet-format格式定义:
    • parquet-mr:Java 实现

(1)Parquet in Action - DDL

  • Hive Table using Parquet
CREATE TABLE lineitem(
    l_orderkey int, l_partkey int, ...
)

(2)Parquet in Action - Spark

  • Load Data using SparkSQL
INSERT INTO lineitem SELECT * FROM tpic10g.lineitem;

image.png

  • Spark 生成的文件会有 .parquet 后缀
  • Hive 生成的文件没有后缀 image.png
  • parquet-cli 工具查看 parquet 文件的具体信息
  • github.com/apache/parq…

(3)Parquet in Action - Parquet vs Text Format

  • Parquet image.png
  • Text image.png

2、Dremel 数据模型

image.png

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

(1)Dremel 数据模型 - Continued

image.png image.png

  • 嵌套类型只保存叶子节点数据
    • 问题:由于列可能是 Optional 和 Repeated,如何把列内的数据对应到逻辑视图里的 Record。

3、数据布局

image.png

  • RowGroup:每一行组包含一定数量或者固定大小的行的集合。
  • ClumnChunk:RowGroup 中按照列切分成多个ColumnChunk。
  • Page:ColumnChunk 内部继续切分成 Page,一般建议 8 kb 大小。压缩和编码的基本单元。
    • 根据保存的数据类型分为:Data Page、Dictionary Page、Index Page
  • Footer 保存文件的元信息
    • Schema
    • Config
    • Metadata
      • RowGroup Meta
      • Column Meta

4、编码 Encoding

image.png

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

image.png

  • 字典编码 Dictionary Encoding:适用于列基数不大的场景,构造字典表,写入 Dictionary Page;把数据用字典 Index 替换,然后用 RLE 编码
  • 默认场景下 Parquet-mr 会自动根据数据特征选择
  • 业务自定义:org.apache.parquet.column.values.factory.ValuesWriterFactory

5、压缩 Compression

image.png

  • Page 完成 Encoding 以后,进行压缩
  • 支持多种压缩算法
  • snappy:压缩速度快,压缩比不高,适用于热数据
  • gzip:压缩速度慢,压缩比高,适用于冷数据
  • zstd:新引入的压缩算法,压缩比和 gzip 差不多,而且压缩速度比肩 snappy
  • 建议选择 snappy 或者 zstd,根据业务数据类型充分测试压缩效果,以及对查询性能的影响

(1)Compression - 对比

image.png

6、索引 Index

image.png

  • 和传统的数据库相比,索引支持非常简陋
  • 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

(1)索引 Index - Bloom Filter

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

(1)排序 - Ordering

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

7、过滤下推 Predicate PushDown

image.png

  • parquet-mr 库实现高效的过滤机制
  • 引擎侧传入 Filter Expression
  • parquet-mr 转换成具体的 Column 的条件匹配
  • 查询 Footer 里的 Column Index,定位到具体的行号
  • 返回有效的数据给引擎侧

8、Spark 集成 - 向量化读

image.png

  • ParquetFileFormat 类
  • 向量化读开关:
    • spark.sql.parquet.enableVectorizedReader
  • 向量化读是主流大数据分析引擎的标准实践,可以极大地提升查询功能
  • Spark 以 Batch 的方式从 Parquet 读取数据,下推的逻辑也会适配 Batch 的方式

9、深入 Dremel 模型

(1)Repetition Level

image.png image.png

  • Repetition Level:该字段在 Field Path 上第几个重复字段上出现
    • 0:标识新的 Record
    • Name.Language.Code 为例,Name 是第一个重复字段,Language 是第二个重复字段

(2)Difinition Level

image.png image.png

  • Definition Level:用来记录在 filed path 中,有多少个字段是不存在(optional/repeated)而实际出现的
  • Name.Language.Code 为例,Name 和 Language 都是可以不存在的
  • 第一个 NULL 字段,D 是 1,说明 Name 是存在的,但是 Language 是不存在的,保留原有的信息

(3)Re-Assembly

image.png image.png

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