这是我参与「第四届青训营 」笔记创作活动的第9天
1. 列存 vs 行村
1.1 数据格式层概述
计算层:各种计算引擎
数据格式层:定义了存储层文件内部的组织形式
存储层:承载数据的持久化存储
计算引擎通过格式层的支持来读写文件
1.2 分层视角下的数据形态
计算层:File、Blocks
格式层:File内部的数据布局layout + Schema
计算引擎:Rows + Columns
1.3 两种数据查询分析场景
| OLTP | OLAP | |
|---|---|---|
| 典型场景 | 在线业务系统,例如:订单、交易、社交、评论等 | 数据仓库或者大数据分析系统,例如:决策分析、BI系统、推荐系统等 |
| 业务特征 | 事务、实时性、低延时、高并发、高可用 | 弱事务性、近实时、离线分析、大吞吐、并发相对不高、可用性可以有一定的妥协 |
| 数据模型特征 | Schema相对简单、数据维度不多、数据规模小(TB级别数据量) | Schema复杂、数据维度很多,几百个column很常见、数据规模巨大(PB EB级别) |
1.4 OLTP: 行式存储格式(行存)
每行的数据在文件上是连续存储的
读取整行数据效率高、单词IO顺序读即可
典型系统:
- 关系型数据库
- Key-Value数据库
1.5 OLAP: 列式存储格式(列存)
每列的数据在文件上是连续存储的
读取整列的效率较高
同列的数据类型一致,压缩编码的效果更好
典型系统:
- 大数据分析系统:SQL on Hadoop、数据湖分析
- 数据仓库:ClickHouse、Greeplum、阿里云MaxCompute
1.6 总结
- 格式层定义了数据的布局,连接计算引擎和存储服务;
- OLTP和OLAP场景差异明显
- 业务场景决定了技术实现,行存适用于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定义
- 支持可选和重复字段
- 支持嵌套格式
嵌套类型只保存叶子节点数据
2.3 数据布局
RowGroup: 每一行组包含一定数量或者固定大小的行集合
ColumnChunk: RowGroup中按照列切分成多个ColumnChunk
Page: ColumnChunk内部继续切分成Page,一般建议8kB
Footer: 保存文件的元信息
2.4 编码
Plain直接存储原始数据
Run Length Encoding(RLE): 适用于列基数不大,重复值较多的场景
Bit-Pack Encoding: 配合RLE编码使用,让整形数字存储的更加紧凑
Dictionnary Encoding字典编码: 适用于列基数不大的场景,构造字典表,写入到Dictionary Page; 把数字用字典Index替换,然后用RLE编码。
2.5 压缩
Page完成Encoding以后,进行压缩
- snappy: 压缩速度快,压缩比不高,适用于热数据
- gzip: 压缩速度慢,压缩比高,适用于冷数据
- zstd: 新引入的压缩算法,压缩比和gzip相当,压缩速度和snappy相当
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
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字段依赖父节点记录额外信息来重新恢复数据
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