Parquet 是一种在现代大数据生态系统中极为流行的列式存储文件格式。它被设计用来高效地存储和处理大规模数据集。
核心思想:列式存储 vs 行式存储
要理解 Parquet 的优势,首先要明白它与传统行式存储(如 CSV、Avro)的区别。
| 特性 | 行式存储 | 列式存储 |
|---|---|---|
| 存储方式 | 将一行的所有数据连续存储。[Row1: A1, B1, C1], [Row2: A2, B2, C2], ... | 将每一列的所有数据连续存储。[Column A: A1, A2, A3, ...], [Column B: B1, B2, B3, ...] |
| 适合场景 | OLTP 事务处理,需要频繁插入、更新、读取整行数据的场景。 | OLAP 分析处理,需要读取大量数据但只关心少数几列进行聚合、过滤的场景。 |
| 例子 | CSV, JSON, Avro | Parquet, ORC |
| 读取模式 | 读取时需要扫描所有列,即使只用到其中几列。 | 可以只读取查询所需的列,跳过其他列,极大减少 I/O。 |
| 压缩效率 | 一般。因为同一行中不同列的数据类型差异大,压缩效果有限。 | 极高。因为同一列的数据类型相同,值通常相似,更容易获得高压缩比。 |
一个简单的比喻:
- 行式存储 像是一本电话簿,每个人的所有信息(姓名、电话、地址)都在一起。
- 列式存储 像是把所有姓名放在一个文件,所有电话放在另一个文件,所有地址放在第三个文件。
Parquet 的核心特性与优势
-
极高的查询性能
- 列裁剪: 当你的查询只需要
SELECT user_id, timestamp时,Parquet 读取器只会从磁盘上读取user_id和timestamp这两列的数据,完全跳过其他列(如email,ip_address等)。这大大减少了磁盘 I/O,是性能提升的关键。
- 列裁剪: 当你的查询只需要
-
高效的压缩和编码
- 由于同一列的数据类型相同,Parquet 可以使用针对该类型优化的编码方式(如字典编码、游程编码、Delta 编码)。
- 编码后的数据再进行压缩(如 Snappy, GZIP, LZ4),压缩比非常高,通常可以将数据缩小到原来的 1/4 到 1/10。这进一步减少了存储成本和 I/O 开销。
-
灵活的架构和强大的类型系统
- Parquet 文件内置了 Schema(模式),描述了数据的结构、数据类型和嵌套关系。
- 它支持复杂的嵌套数据结构(如数组、映射、结构体),通过 DLP(Dremel 的嵌套列存储算法) 将其高效地展平为列式存储。这使得它非常适合存储来自 JSON、Protocol Buffers 等半结构化数据。
-
谓词下推
- 这是一个非常强大的优化技术。Parquet 文件为每个数据块存储了统计信息(如某列的最小值、最大值)。
- 当执行一个带过滤条件的查询时(如
WHERE age > 30),查询引擎可以在实际读取数据之前,先检查这些统计信息。如果发现某个数据块的最大age是 25,那么整个数据块都会被跳过,根本不会被加载到内存中。
-
与大数据生态无缝集成
- Parquet 是 Apache Hadoop、Spark、Flink、Hive、Impala、Presto/Trino 等主流大数据处理框架的首选文件格式。这些框架都对 Parquet 提供了原生、高度优化的支持。
Parquet 的文件结构
一个 Parquet 文件在物理上是由多个部分组成的:
- 行组: 文件被水平分割成多个行组。每个行组包含数据的一个子集。
- 列块: 在一个行组内,数据被垂直分割成列块。每个列块包含某一列的数据。
- 页: 列块又被分成一系列的页。页是编码、压缩和访问的最小单元。常见的页类型有数据页、字典页等。
- 页头: 包含页的元数据,如编码、压缩、未压缩大小等。
- Footer(文件页脚): 这是文件的“目录”,包含至关重要的元信息:
- File MetaData: 文件的 Schema。
- RowGroup MetaData: 每个行组的信息,以及每个列块的统计信息(最小值、最大值、空值数量等),这些正是谓词下推的基础。
+---------------------------------------+
| Magic Number "PAR1" (4 bytes) |
+---------------------------------------+
| Row Group 1 |
| +---------------------------------+ |
| | Column A Chunk (Pages) | |
| +---------------------------------+ |
| | Column B Chunk (Pages) | |
| +---------------------------------+ |
| | ... | |
| +---------------------------------+ |
+---------------------------------------+
| Row Group 2 |
| +---------------------------------+ |
| | Column A Chunk (Pages) | |
| +---------------------------------+ |
| | ... | |
| +---------------------------------+ |
+---------------------------------------+
| ... (More Row Groups) |
+---------------------------------------+
| Footer (Metadata) |
| +---------------------------------+ |
| | Schema, Row Groups, Statistics | |
+---------------------------------------+
| Footer Length (4 bytes) |
+---------------------------------------+
| Magic Number "PAR1" (4 bytes) |
+---------------------------------------+
何时使用 Parquet?
强烈推荐在以下场景使用:
- 数据仓库和数据分析: 这是 Parquet 的主场,特别是当你需要频繁地对大型数据集进行聚合、过滤和扫描时。
- 数据湖存储: Parquet 是数据湖(如 AWS S3, ADLS)中事实上的标准存储格式。
- 作为 ETL 过程的中间格式或最终输出。
- 需要长期归档大量数据,以节省存储成本。
可能不适合的场景:
- 需要频繁进行单行读写或更新的 OLTP 场景。
- 需要流式写入: Parquet 是为批量写入优化的,不适合一条一条地实时写入。
- 数据量非常小: 对于小文件,Parquet 的元数据开销可能抵消其优势。
一个简单的代码示例(使用 PySpark)
# 读取 CSV 文件(行式)
df = spark.read.csv("path/to/large_file.csv", header=True, inferSchema=True)
# 将其保存为 Parquet 格式(列式)
df.write.parquet("path/to/output_parquet")
# 读取 Parquet 文件
df_parquet = spark.read.parquet("path/to/output_parquet")
# 执行一个只查询特定列的查询,Parquet 的列裁剪会生效
result = df_parquet.select("user_id", "timestamp").where(df_parquet.age > 30)
result.show()
总结
| 特性 | 描述 | 带来的好处 |
|---|---|---|
| 列式存储 | 按列而非按行存储数据。 | 查询性能快(列裁剪),压缩效率高。 |
| 丰富的元数据 | 文件内包含 Schema 和列块统计信息。 | 谓词下推,跳过无关数据。 |
| 高效的编码 | 针对不同数据类型使用特定编码。 | 更高的压缩比,更快的扫描速度。 |
| 开源与通用 | 被所有主流大数据工具支持。 | 生态系统兼容性好,无供应商锁定。 |
总而言之,Parquet 是大数据分析和数据湖领域不可或缺的基石技术。如果你在处理 TB/PB 级别的数据,并专注于分析型工作负载,选择 Parquet 几乎总是一个正确的决定。
1790

被折叠的 条评论
为什么被折叠?



