Skip to content
团子云技术 Lite 1.048596
Go back

存储架构师视角:Parquet、Iceberg 与湖仓一体

理解 Parquet 和 Iceberg,本质上就是理解大数据计算引擎如何在对象存储之上,通过精妙的物理数据布局和逻辑元数据索引来榨取性能、实现类似关系型数据库(ACID)的功能。

整个数据湖架构可以分成三层:存储底座(对象存储)→ 物理文件格式(Parquet)→ 逻辑表格式(Iceberg)

Parquet:榨干磁盘与网络 I/O 的物理文件格式

Parquet 是一种列式存储格式。在对象存储的语境下,它解决的核心问题是减少 HTTP GET 请求的数据量

如果数据是 CSV 或 JSON(行存),查 select age from table 需要扫描整个文件。Parquet 将同一列的数据连续存储,查 age 列只需读取那一列的数据块。又因为同一列的数据类型相同,Parquet 可以上 RLE(游程编码)、字典编码等高效编码,配合 Snappy/Zstd 压缩,压缩比轻松到 10:1 以上。

对对象存储最友好的设计是 Footer

Parquet 的元数据放在文件尾部,记录了各个列块(Column Chunk)在文件中的 Byte Offset,以及该块内数据的 Min/Max 值。计算引擎(如 Spark)先发起一个 Range GET 读文件末尾几 KB 拿到 Footer,解析出需要的列所在的 Offset 和长度,再发起并行的 Range GET 精准拉取数据块——完美避开下载整个大对象。

Iceberg:对象存储上的”分布式数据库索引”

如果只有 S3 + Parquet,会卡在一个地方:对象存储的 ListObjects API 太慢。传统的 Hive 架构依赖目录(Prefix)划分分区,查询时需要疯狂 List 目录来找文件,在对象存储上是灾难。

Iceberg 的核心思想是 Track Files, Not Directories——放弃依赖底层目录树结构,用一棵严格的元数据树来记录每次 Commit 产生了哪些文件。

元数据层级:

层级格式内容
Metadata File.jsonSchema、分区配置、当前 Snapshot 指针
Manifest List.avro当前快照包含哪些 Manifest 文件
Manifest File.avro灵魂所在——底层 Parquet 文件的 S3 URI + 每列 Min/Max 统计

where id = 500 时,Iceberg 读这棵元数据树,利用 Min/Max 直接过滤掉不可能包含 500 的 Parquet 文件(Data Skipping)。原本需要 1000 次 ListObjects 的操作,变成了几次精准的 GetObject 读元数据,然后拿到目标 Parquet 文件的确切 URL。

ACID 与时间旅行也源于此——每次写入生成全新的元数据文件(MVCC),读写互不干扰,随时可以把指针切回旧的 Metadata JSON,实现”时间旅行”查询。

三个核心特性是怎么在对象存储上实现的

1. 事务(ACID)

两个 Spark 任务同时往一个 Prefix 写数据,或者写到一半崩溃,对象存储天然没有跨对象的分布式事务。Iceberg 的解法是 MVCC + Catalog 原子替换

写完一堆 Parquet 和新的元数据 JSON(比如 v2.json)后,Iceberg 向 Catalog(Hive Metastore、JDBC 或对象存储的条件写锁)发起一个原子操作——把表版本指针从 v1.json 切到 v2.json。正在跑的查询继续读 v1.json(快照隔离),指针一切换,新查询立刻看到 v2.json。写入崩溃了直接删未提交文件,原表毫发无伤。

2. 细粒度 Update/Delete

对象存储不支持修改对象内部某几行。用户 DELETE FROM table WHERE id = 10,传统做法是整分区重写。Iceberg 给了两种策略:

策略做法适用场景
Copy-on-Write读 Parquet → 内存删行 → 写新 Parquet,元数据指向新文件读多写少
Merge-on-Read不碰原文件,追加一个 Delete File(记录”文件 A 第 100 行被删”),查询时 Join 过滤高频写

Merge-on-Read 把”随机写/大文件重写”变成了顺序追加小文件,对对象存储极其友好。

3. Schema 演进

传统 Hive 架构中,列名一改(user_iduid),旧 Parquet 文件因内部还写着 user_id,直接读出 Null。Iceberg 给每个列分配了唯一的、自增的 ID,与列名和位置完全无关。

ALTER TABLE RENAME COLUMN user_id TO uid 只改最新的元数据 JSON,记录”ID 1 现在叫 uid”,底层 Parquet 文件不需要任何重写。下游引擎读旧 Parquet 时,Iceberg 根据 ID 1 自动把文件里的 user_id 映射为 uid。增删列、改列顺序,全部是毫秒级元数据操作。

元数据持久化层:高性能 SSD 集群对吗?

Iceberg 的元数据文件(.json.avro)确实持久化在对象存储上。但最顶层的”根指针”不能完全依赖普通对象存储——原子 CAS(Compare and Swap)需要条件乐观锁,传统 S3 只支持 PUT 覆盖。所以 Iceberg Catalog 通常用关系型数据库(行锁做原子替换)或支持 If-Match 条件的现代对象存储来承载。

搞高性能 SSD 集群存元数据,不仅合理,而且是当前云厂商和大厂存储团队的核心优化方向。Iceberg 架构带来了严重的 Metadata Amplification——一个复杂 SQL 进来,引擎需要先发起好几次 HTTP 请求读 JSON、读 Manifest List、读 Manifest 块,全解析完才能开始读数据。这些元数据文件都是几 KB 到几 MB 的小文件,对象存储如果用 HDD 底座,TTFB 和 QPS 根本撑不住。

业内三种主流方案:

方案做法代表
分层存储池网关根据 Prefix/后缀自动路由到 NVMe SSD 池 vs HDD EC 池自研对象存储内核优化
极速型产品全 SSD 单区部署,个位数毫秒延迟AWS S3 Express One Zone
独立缓存层存算之间架 Alluxio/JuiceFS 做 SSD 缓存,异步刷回存算分离架构标配

行存 vs 列存:从存储架构师角度看

抛开”查询变快/变慢”的表象,行存(NSM)和列存(DSM)本质上是数据在物理介质上的内存对齐方式、信息熵,以及由此引发的 I/O 放大与 CPU 指令流问题

行式存储(NSM):面向元组的生命周期管理

行存把同一行的所有属性在物理字节空间上连续存放。存储引擎中,磁盘被划分为固定大小的 Page(如 16KB),采用 Slotted Page 结构:Page Header → Line Pointers(槽位数组,存每个 Tuple 的偏移量)→ Tuples(含隐藏列如 TxID、Rollback Pointer 和用户列)。

优势:

劣势:

列式存储(DSM):面向分析计算的极致榨取

列存把所有行中相同属性的数据物理连续存放。在 Parquet 的 Column Chunk 或内存结构中,列存本质上退化成了同构类型的强类型数组(Dense Arrays)

优势:

劣势:

解法就是所有列存引擎(ClickHouse、Iceberg、传统数仓)只支持批量追加,或引入 LSM-Tree 结构(Delta Lake / Iceberg 的 Merge-on-Read)用小文件追加替代原地修改。

PAX:合久必分,分久必合

纯粹的 NSM 或 DSM 在现代复杂业务中都无法包打天下。Parquet、ORC 以及现代 HTAP 数据库实际采用 PAX(Partition Attributes Across)——混合行列存储:

这保持了列存的高压缩比和 SIMD 优势,又极大缓解了元组重组代价——SELECT * 时引擎只需将一个 Row Group 读入内存,所有列都在这几十 MB 连续空间内,重组变成纯内存拷贝。

OLTP 用行存,OLAP 用列存——不是选择,是必然

核心业务系统(OLTP)和数据分析系统(OLAP)选不同存储模型,根源在于 I/O 访问模式、数据局部性、计算密集度 上不可调和的矛盾。

OLTP:面向实体的生命周期管理

业务系统处理数据的最小单元是”一个完整的对象”——一笔订单、一个用户。特点是高并发、多点查、频繁增删改、强 ACID。

行存对 OLTP 是唯一解:

OLAP:面向宏观指标的聚合与降维

分析师不关心某个用户叫什么,关心的是”过去一年华东区 18-25 岁用户平均客单价”。特点是低并发、海量扫描、极少更新、宽表中只查少数列。

列存对 OLAP 是降维打击:

在物理世界,数据在磁盘上的排列方式只能有一种。所以传统架构师会建两套系统:MySQL(行存)撑业务,CDC 把数据同步到 ClickHouse / Iceberg(列存)做分析。

2026 年 OLAP 生态

站在 2026 年,ClickHouse 依然是顶流,但不再是所有 OLAP 场景的默认唯一解。生态从”百家争鸣的独立数据库”演变成了”围绕数据湖的计算引擎大乱斗”。

ClickHouse:单表暴力扫描和实时日志的王者。 没有没落,在日志分析(Observability)、时序指标、宽表极速查询上依然统治力极强。全面拥抱了存算分离,底层深度绑定对象存储。边界也彻底被认清——依然不擅长复杂跨表多维 JOIN,运维门槛不低。更多被收敛在”实时数仓”和”高频点查”的特定流水线里。

DuckDB:单机与边缘计算的绝对黑马。 随着单台服务器内存动辄上 TB,80% 的分析场景根本不需要分布式集群。DuckDB 就像分析领域的 SQLite——数据分析师的 Python 脚本、BI 工具的本地缓存、浏览器 WASM 端、数据清洗流水线(dbt)都在内置它。它可以直接通过 HTTP Range GET 极速拉 S3 上的 Parquet 文件做本地计算,这种”无服务”查询模式让大量并发压力直接打到对象存储网关上。

StarRocks / Apache Doris:统领湖仓一体与复杂 JOIN。 分布式 MPP 领域,中国的开源力量在 2026 年已占全球半壁江山。它们完美填补了 ClickHouse 的短板——极其强悍的多表复杂 JOIN 能力,以及极低延迟的实时微批写入。现在企业不再把所有数据都导进 OLAP 引擎,StarRocks/Doris 更多作为”计算网关”存在,内部只存最热的实时数据,历史数据直接外表关联 S3 上的 Iceberg 查。

模块化:没人再从头手写执行引擎了。 Meta 开源的 Velox C++ 向量化执行引擎统一了底层计算逻辑。大家都在用标准的 Arrow 内存格式和 Velox 拼装自己的 OLAP 系统,竞争的焦点变成了”谁的优化器(CBO)写得好”和”谁跟底层对象存储结合得更紧密”。

2026 年 OLAP 生态最大的共识:Storage is commodity, Compute is ephemeral, Format is king(存储是基础设施,计算是弹性的,格式才是王道)。所有 OLAP 引擎都失去了垄断数据的特权,底层数据统一沉淀在 S3 + Iceberg + Parquet 上。对象存储系统已成为企业真正意义上的 Single Source of Truth。

几点心得

  1. Parquet 的设计哲学是”硬件感知”——Footer 放末尾让 Range GET 精准拉取,列存布局让压缩和 SIMD 火力全开。好的文件格式是算出来的,不是拍出来的。
  2. Iceberg 解决的是”元数据问题”——对象存储能无限存文件,但找文件很慢。Iceberg 用一棵精心设计的元数据树把 ListObjects 问题变成了 O(log n) 的元数据查询,这才是它作为表格式的真正价值。
  3. 行存和列存没有优劣,只有场景匹配。OLTP 要的是”一次 I/O 拿整行”,OLAP 要的是”海量数据中只碰有用的列”。理解了 I/O 模式就理解了一切。
  4. 对象存储是整个数据生态的底座。所有 OLAP 引擎最终都在围着 S3 转——S3 Select 把算力下推到存储节点、Athena 做存算分离查询、DuckDB 直接拉文件本地算。对对象存储开发来说,理解上游引擎的行为,比优化底层 I/O 路径本身更重要。

如果有什么不对,欢迎指正。


Share this post on:

Next Post
收费站与 Little's Law:分布式系统容量规划的数学底线