这是我参与「第四届青训营」笔记创作活动的第12天,学习内容为《Parquet 与 ORC:高性能列式存储》,内容包括 列存储与行存储、Parquet与ORC、列存储演进。
思维导图如下:
列存储与行存储
- 数据格式层概述
- 数据格式层: 定义了存储层文件内部的组织格式,计算引擎通过格式层的支持来读写文件
- 分层视角下的数据形态
- 存储层:File、Blocks
- 格式层:File内部的数据布局(Layout+Schema)
- 计算引擎:Rows+Columns
- 两种数据查询分析场景:OLTP VS OLAP
- OLTP:在线业务系统,例如:订单、交易、社交、评论,适合行式存储格式(关系型数据库、key-val数据库)
- OLAP:数仓或者大数据分析系统,例如:决策分析、BI系统、推荐系统等,适合列式存储格式(读取效率高、压缩编码效率更好;大数据分析系统、数据仓库)
Parquet与ORC
Parquet
- Dremel数据模型
只有叶子节点数据会被保存在数据文件里
- r:Repetition Level,该字段在Field Path上第几个重复字段上出现;两个0之间是这一行的数据
- d:Definition Level,该字段记录在field path中,有多少个字段是可以不存在而实际出现的 3.根据全部或者部分列数据重新构造Record
- 数据布局
- 编码
- Run Length Encoding(RLE):适用于列基数不大,重复值较多的场景,例如:bool、枚举、固定的选项
- Bit-pack ENcoding:配合RLE,二进制位存储cnt
- 字典编码:适用于列基数不大的场景,存储字符串。构建字典表,数据用字典index替换,然后用RLE编码
- 压缩
- snappy:压缩速度快,压缩比不高,适用于热数据
- gzip:压缩速度慢,压缩比高,适用于冷数据
- zstd:压缩比和gzip差不多,压缩速度比肩snappy
- 索引
- min-max index
- cloumn index
- offset index
- bloom filter:对于列基数比较大的场景,或者非排序列的过滤
- 过滤下推:在格式层过滤掉大多数不相关的数据、减少真实的读取数据量
ORC
- 数据模型:语法树,每个节点都创建一个column
- 数据布局
- rooter+stripe+column+page结构
- encoding/compression/index支持上和parquet几乎一致
Pqrquet vs ORC
- 从原理层面,最大的差别就是对于 NestedType 和复杂类型处理上
- Parquet 的算法上要复杂很多,带来的 CPU 的开销比 ORC 要略大
- ORC 的算法上相对加单,但是要读取更多的数据
- Parquet 在复杂 Schema 场景下的算法开销影响较大
- 在 Spark 场景下 Parquet 工作的更好;在 Hive 场景下,ORC 更好
列存储演进
- 数仓中的列存储
- ClickHouse的MergeTree引擎也是基于列存构建的
- 支持更加丰富的索引
- 湖仓一体的大趋势:列存格式趋同
- 存储侧下推
- 更多的下推工作下沉到存储服务侧
- 越接近数据,下推过滤的效率越高
- 挑战:存储侧感知Schema、计算生态的兼容和集成
- Column Family支持
- 背景:Hudi数据湖场景下,支持部分列的快速更新
- 在 Parquet 格式里引入 Column Family 概念,把需要更新的列拆成独立的 Column Family
- 深度改造 Hudi 的 Update 和 Query 逻辑,根据 Column Family 选择覆盖对应的 Column Family