[控场AI]
· 7 分钟阅读· 3,804 字

同一个任务,两种引擎:InnoDB 与 Iceberg 内部机制对比

同一个任务,两种引擎:InnoDB 与 Iceberg 内部机制对比

InnoDB 与 Iceberg 面对同一存储索引难题,因场景约束不同而走出相反的实现路径。

文章将 MySQL 的 InnoDB 与数据湖表格式 Iceberg 放在同一框架下解剖,指出二者虽分属数据库引擎与表格式,但都在解决存储、索引、元数据描述与并发控制这同一道题。InnoDB 以 16KB 页、聚簇 B+ 树、redo/undo 日志和 MVCC 实现在线事务,复杂度来自“就地更新”这一前提;Iceberg 以 table metadata、manifest list、manifest、snapshot 和原子提交在不可变对象存储上重建表语义,简洁来自“文件不可变”这一前提。文章通过对照表展示两者在数据组织、并发控制、变更日志、读路径优化和历史版本上的相反选择,并强调对比的价值在于分辨哪些是本质约束、哪些只是场景选择,从而指导存储选型与迁移决策。

当同一个任务交给两种引擎

在数据世界里,有一类问题反复出现:如何高效地存储、索引并查询海量结构化数据?MySQL 的 InnoDB 和 Apache Iceberg 给出了截然不同的答案。前者是服务在线事务几十年的行式存储引擎,后者是为数据湖而生的开放表格式。RSS 源中一篇题为《Same Job, Two Engines: InnoDB and Iceberg Internals》的讨论,把这两个看似平行的系统放在同一张手术台上解剖。

rss source: Same Job, Two Engines: InnoDB and Iceberg Internals

它们真正要解决的其实是同一道题

把 InnoDB 和 Iceberg 放在一起比较,很多人第一反应是“一个是数据库引擎,一个是表格式,有什么可比性”。这种划分来自它们的使用场景,而不是它们要解决的问题。剥开场景这层壳,两者的目标高度重合:

  • 在不可靠的存储之上提供可靠的数据结构;
  • 用元数据描述数据文件的组织方式;
  • 让查询引擎能够只读它真正需要的那部分数据;
  • 在写入和读取之间做并发控制。

InnoDB 用 B+ 树页、redo log、undo log 和 MVCC 来完成这件事;Iceberg 用 manifest 文件、manifest list、snapshot 和元数据 JSON 来完成这件事。任务相同,实现路径几乎处处相反。

InnoDB 的内部机制:为了在线事务而生

InnoDB 的设计哲学可以概括为“状态驻留在内存和磁盘页中,变更靠日志先行”。

页、B+ 树与聚簇索引

数据按 16KB 的页组织,表本身是一棵以主键为序的 B+ 树,也就是聚簇索引。这意味着主键查询几乎是一次页定位,而二级索引必须回表。行数据、索引、事务信息都挤在同一个页结构里,读写路径非常短。

聚簇索引(Clustered Index) 与 二级索引回表 是理解 InnoDB 读写代价的关键。聚簇索引指行数据本身按主键顺序存放在 B+ 树的叶子节点上,因此主键点查只需从根页逐层定位到叶子页,即可直接拿到整行数据。二级索引的叶子节点只存索引列值与主键值,若查询列超出索引覆盖范围,就必须用得到的主键再去聚簇索引里查一次完整行,这个过程称为“回表”。回表的代价在于多一次 B+ 树遍历,且可能触发随机 I/O。为减少回表,InnoDB 支持覆盖索引:当查询所需列都包含在二级索引中时,可直接返回,无需访问聚簇索引。这也解释了为什么主键长度会影响所有二级索引的体积——每个二级索引项都要附带主键值。

日志先行与崩溃恢复

任何修改先写 redo log,再异步刷脏页。崩溃后通过 redo 重放恢复,通过 undo 实现回滚和多版本读。InnoDB 把“数据文件”和“修改日志”分离得很清楚,用空间复杂度换取在线事务的稳定延迟。

MVCC 与并发

InnoDB 的多版本不是存在独立文件中,而是就地修改行、用 undo 链跟踪旧版本。读操作根据事务可见性判断应该看哪一版。这种设计让单行更新非常快,代价是历史版本与当前数据耦合在一起,清理需要 purge 线程。

MVCC(多版本并发控制) 的核心目标是让读操作不阻塞写、写操作不阻塞读,从而在保证隔离性的同时维持高并发。在 InnoDB 中,每行记录隐藏了事务 ID 和回滚指针,回滚指针串起 undo log 中的旧版本,形成版本链。当一个读事务访问某行时,会沿着版本链找到对当前事务可见的那一版:已提交读(RC)每次语句执行都生成新快照,可重复读(RR)在事务开始时生成快照。可见性判断依据事务 ID 与 Read View 的比较。旧版本不会自动消失,需要 purge 线程在确认没有更早的活跃事务引用它们后异步清理。这种“就地更新 + undo 链”的方案使单行写延迟极低,但长事务会拖住 purge,导致 undo 空间膨胀。

Iceberg 的内部机制:为数据湖重建表语义

Iceberg 面对的是一个完全不同的问题:数据躺在 S3 或 HDFS 上,对象存储不可变、没有事务、没有索引。它的解法是把所有可变的元信息抽出来,放进分层元数据文件。

元数据分层

table metadata JSON 指向 manifest list,manifest list 指向多个 manifest file,manifest file 记录每个数据文件的路径、分区、列级统计和文件级统计。查询规划时先读元数据,用分区和统计信息裁剪掉不需要读的文件。

Manifest 文件与列级统计 是 Iceberg 实现查询裁剪的基石。每个 manifest file 是一份 Avro 文件,里面记录了若干 data file 的条目,每个条目包含文件路径、所属分区、记录数、文件大小,以及每列的上下界、空值计数等统计信息。查询规划时,Iceberg 先读 table metadata 定位 manifest list,再根据分区转换结果和查询谓词跳过无关 manifest,最后在 manifest 内部用列级 min/max 进一步剔除数据文件。这种“先元数据、后数据”的顺序,使得一次分析查询可能只扫描总数据量的百分之几。统计信息的准确性直接决定裁剪效果,因此 Iceberg 还提供 Puffin 等索引格式来补充 Bloom filter 或更细粒度的统计。

Snapshot 与原子提交

每次写入产生一个新 snapshot,旧 snapshot 保留历史。提交的原子性靠“重命名元数据文件”或者外部 catalog 的条件更新实现。这相当于用一次指针切换,替代了 InnoDB 的页级并发控制。

对象存储上的原子提交 是 Iceberg 面对的核心工程难题。S3 等对象存储只保证单对象 PUT 的原子性,没有跨对象的 rename 或事务。Iceberg 的解法是:每次写入先生成新的 manifest 与 manifest list,再生成新的 table metadata JSON,最后通过一次原子操作把 catalog 中的“当前元数据指针”切换到新文件。常见实现有三种:Hive Metastore 用数据库事务做条件更新,REST catalog 用服务端 CAS,纯文件系统方案则依赖 rename 的原子性。旧 snapshot 并不会被立即删除,而是保留在 metadata 中供时间旅行查询使用,直到显式执行 expire snapshots。这种“写新文件、换指针”的模式把并发控制从页级锁简化成了元数据级乐观并发。

隐藏分区与 schema 演进

Iceberg 把分区信息从物理目录中剥离,用户不再依赖 Hive 那种 dt=2024/region=cn 的目录结构。分区可以作为元数据里的变换表达式存在,schema 演进也不需要重写历史数据。

对照表:同一任务的两种解法

维度InnoDBIceberg
数据组织行式页 + B+ 树列式文件 + 元数据树
并发控制行锁 + undo + MVCC快照隔离 + 原子提交
变更日志redo / undo无逻辑日志,靠新快照
读路径优化索引 + 缓存池文件级谓词下推 + 统计裁剪
历史版本undo 链旧 snapshot + manifest
存储假设本地或块存储对象存储

这张表也解释了两者各自擅长什么:InnoDB 擅长高频小事务和低延迟点查,Iceberg 擅长海量数据批量写入、分析型扫描和 schema 演进。

为什么对比比单独理解更有价值

单看 InnoDB,你会觉得“表必须有行锁、必须有 redo log”;单看 Iceberg,你会觉得“表只是一组文件加元数据”。把两者放在一起,才能看清哪些是本质约束、哪些只是场景选择。

InnoDB 的复杂度来自“就地更新”这个前提——只要允许原地修改,就必须有 CRUD 的日志和回滚机制。Iceberg 的简洁来自“文件不可变”这个前提——只要每次写入产出一批新文件,提交就退化成一次指针切换。

对工程师来说,这种对比有两层启发。第一层是设计层面:选存储方案时要先确定写入模式和存储介质,再决定元数据形态。第二层是迁移层面:把在线业务从 InnoDB 搬到 Iceberg 并不只是换连接串,而是要重新定义一致性、延迟和成本之间的权衡。

结语

同一道题,InnoDB 用页、日志和锁写下了 OLTP 的标准答案,Iceberg 用元数据、快照和不可变文件写下了数据湖的答案。理解它们的内部机制,不是为了分出高下,而是为了在面临具体任务时,知道自己在向哪一种约束妥协。

#InnoDB#Apache Iceberg#存储引擎#MVCC#数据湖#表格式#元数据#快照隔离
分享:

相关推荐