pgrust 快照管理:snapmgr 与 MVCC 可见性判断实现

发布时间:2026/8/18 15:25:43
pgrust 快照管理:snapmgr 与 MVCC 可见性判断实现 pgrust 快照管理snapmgr 与 MVCC 可见性判断实现【免费下载链接】pgrustPostgres rewritten in Rust, now faster than Postgres and Clickhouse项目地址: https://gitcode.com/GitHub_Trending/pg/pgrustpgrust 是一个用 Rust 重写 PostgreSQL 的开源项目它的目标不仅是翻译 C 代码更要在保证语义一致的前提下发挥 Rust 的内存安全优势。在众多底层机制中快照Snapshot管理和MVCC 可见性判断是数据库并发控制的核心——它决定了谁能看到哪一行数据。本文面向初学者用通俗的方式拆解 pgrust 中 snapmgr 模块与 MVCC 可见性判断的实现思路。一、什么是数据库快照Snapshot简单说快照就是某一时刻数据库的一致视图。在 MVCC多版本并发控制模型下读操作不会阻塞写操作每个事务看到的都是自己启动时那一刻的数据状态。这个状态就是由快照来描述的。在 pgrust 中快照的核心数据结构SnapshotData定义在 snapshot.rs它对应 PostgreSQL 的utils/snapshot.h。一个快照主要由以下字段构成字段含义类比xmin所有小于 xmin 的事务都已提交一定看得见的底线xmax所有大于等于 xmax 的事务都不可见一定看不见的上限xip正在进行中的事务 ID 数组灰色地带subxip进行中的子事务 ID 数组灰色地带的细分curcid当前命令 ID我自己改到哪一步了 记忆技巧xmin 是下界xmax 是上界xip 是中间那些还没下结论的。二、MVCC 可见性判断的核心原理MVCC 的可见性判断本质上就是回答一个问题这条元组tuple的插入事务和删除事务对于当前快照来说状态是什么判断流程大致如下如果元组的插入事务 IDxmin小于当前快照的 xmin说明它早已提交 →可见如果插入事务 ID 大于等于快照的 xmax说明它太新 →不可见如果插入事务 ID 落在 xip 列表中说明该事务还在进行中 →不可见只有当插入事务已提交、且删除事务未提交或不存在时元组才可见pgrust 把这段逻辑实现在 heapam_visibility 模块中它对应 PostgreSQL 的heapam_visibility.c。这里值得一提的是pgrust 用HTSV_Result枚举如HEAPTUPLE_DEAD、HEAPTUPLE_LIVE、HEAPTUPLE_RECENTLY_DEAD来表达真空VACUUM视角下的元组状态判别值顺序与 C 枚举完全一致保证了对齐迁移的兼容性。三、pgrust 的 snapmgr 模块在哪里快照的生产、登记、销毁都由snapmgr负责它的实现在主要逻辑crates/backend/utils/time/snapmgr/src/lib.rs快照类型定义crates/_support/types/snapshot/src/snapshot.rs数据采集对应 C 的GetSnapshotDatacrates/backend/storage/ipc/procarray/src/snapshot.rs有意思的是pgrust 的代码注释里明确标注了每个函数对应的 C 源文件例如utils/time/snapmgr.c、utils/snapshot.h这让熟悉 PostgreSQL 的开发者可以无缝对照阅读。四、三种常用快照的获取方式snapmgr 提供了多个入口函数最常用的有以下三类1. 事务快照GetTransactionSnapshot对应GetTransactionSnapshot()在事务第一次执行查询时生成整个事务期间保持不变。它保证了事务内读一致性——同一个事务里多次查询看到的数据是一致的。2. 活跃快照GetActiveSnapshot对应GetActiveSnapshot()返回当前压入活跃快照栈顶部的快照。事务快照生成后会被压栈执行器通过它来读取数据。3. 目录快照GetCatalogSnapshot对应GetCatalogSnapshot(relid)专用于系统目录的读取配合失效消息invalidation机制保证 DDL 与系统表读操作的一致性。五、快照的生命周期管理快照不是用完即弃的它需要严格的登记与释放机制否则会带来内存泄漏或可见性错误。snapmgr 提供了两套配套机制活跃快照栈Active Snapshot StackPushActiveSnapshot把快照压入栈顶PopActiveSnapshot弹出栈顶快照栈式结构天然匹配函数嵌套调用的场景比如 SPI 调用、触发器嵌套等注册快照Registered SnapshotsRegisterSnapshot登记一个快照让它在事务期间保活UnregisterSnapshot释放登记pgrust 用Vec扫描方式替代了 C 版本中的配对堆pairing heap代码更简洁也更容易验证正确性六、一个直观的执行流程把上面的概念串起来一次普通 SELECT 查询的快照流程大概是BEGIN └─ 第一次查询 ├─ GetTransactionSnapshot() 生成事务快照 ├─ PushActiveSnapshot() 压入活跃栈 ├─ 执行器按 xmin/xmax/xip 判断每行可见性 ├─ PopActiveSnapshot() 弹出快照 └─ 事务结束统一释放注册快照 COMMIT七、pgrust 实现上的几个亮点所有权清晰SnapshotData中的xip、subxip用 Rust 的Vec管理替代了 C 的裸指针数组从根本上消除了越界风险。语义保留虽然换了数据结构但xcnt、subxcnt等计数字段仍然显式保留保证字段级语义与 C 版本完全对齐。渐进式迁移项目通过大量seam接缝模块逐步替换 C 实现例如snapmgr内部仍保留elog_error等错误处理辅助函数风格与 PostgreSQL 保持一致。八、总结快照管理是 MVCC 的地基而 MVCC 又是 PostgreSQL 并发模型的灵魂。pgrust 用 Rust 重写这一部分时既忠实还原了 xmin/xmax/xip 的可见性判断规则又借助 Rust 的类型系统让快照的内存管理更安全。对于想学习数据库内核、或者关注 Rust 重写大型 C 项目的读者来说pgrust 的 snapmgr 与可见性判断实现是一份非常值得精读的活教材。如果你也想动手研究克隆仓库后从crates/backend/utils/time/snapmgr/src/lib.rs开始读起再对照crates/backend/access/heap/heapam_visibility/src/lib.rs中的可见性判断很快就能建立起 MVCC 的完整认知。【免费下载链接】pgrustPostgres rewritten in Rust, now faster than Postgres and Clickhouse项目地址: https://gitcode.com/GitHub_Trending/pg/pgrust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考