postgresql mvcc通过元组多版本、事务快照和可见性规则实现读写不互斥:xmin/xmax标记版本生命周期,t_ctid构建版本链,vacuum异步清理旧版本,不同隔离级别仅调整快照获取时机。

一、MVCC 的核心设计目标
PostgreSQL 采用多版本并发控制(MVCC)机制,旨在解决高并发场景下读写操作相互阻塞的根本矛盾。传统锁机制要求读操作等待写锁释放,或写操作等待读锁释放,而 MVCC 通过为每行数据维护多个历史版本,使读操作可访问事务启动时刻已存在的旧版本,写操作则生成新版本,二者在逻辑上完全分离。
1、每个事务启动时获取一个快照,该快照固化了其可见的数据范围;
2、所有 SELECT 操作依据该快照进行可见性判断,不加任何行级或表级锁;
3、INSERT、UPDATE、DELETE 操作均不覆盖原数据,而是生成带事务标识的新元组;
4、旧版本元组的物理清理由 VACUUM 进程异步完成,与事务执行解耦。
二、元组头部关键字段的作用机制
PostgreSQL 的 MVCC 实现依赖于每行数据(HeapTuple)头部嵌入的系统字段,这些字段构成版本链与可见性判断的基础。它们不占用用户定义列空间,由存储引擎自动管理,是实现无锁读的核心元数据。
1、xmin 字段记录插入该行的事务ID,作为该版本的“出生时间戳”;
2、xmax 字段记录删除或更新该行的事务ID,值为0表示当前有效,非0则标记为逻辑删除;
3、t_ctid 指向同一行最新版本的物理位置,形成前向版本链;
4、t_infomask 中的 Hint Bits 缓存 xmin/xmax 对应事务的提交状态,避免频繁查 CLOG 日志。
三、事务快照与可见性判定流程
事务快照是 MVCC 隔离性的基石,它定义了事务“看到的世界”。快照包含三个核心要素:xmin(当前活跃事务中最小事务ID)、xmax(下一个待分配事务ID)和 xip 数组(当前所有活跃事务ID列表)。可见性判断函数 HeapTupleSatisfiesMVCC 严格依据此快照与元组头部字段比对执行。
1、检查 xmin 事务是否已提交:先查 Hint Bits,若未缓存则查 CLOG 提交日志;
2、若 xmin 事务未提交或已回滚,则该元组对当前事务不可见;
3、若 xmin ≥ 当前快照的 xmax,说明该版本在当前事务启动后才被创建,不可见;
4、若 xmax 非零,需进一步判断 xmax 事务是否已提交且是否在 xip 数组中——若已提交且不在活跃列表中,则该元组已被逻辑删除,不可见。
四、不同隔离级别下的 MVCC 行为差异
PostgreSQL 完全支持 SQL 标准定义的四种隔离级别,但其实现全部基于同一套 MVCC 基础设施,仅通过快照获取时机与可见性规则微调来区分语义。各隔离级别不改变底层版本存储方式,仅改变事务视角。
1、READ COMMITTED 级别下,每次查询语句开始时重新获取快照,因此同一事务内多次 SELECT 可能返回不同结果;
2、REPEATABLE READ 级别下,事务启动时获取一次快照并全程复用,保证多次读取结果一致;
3、SERIALIZABLE 级别在 REPEATABLE READ 基础上增加谓词锁与冲突检测,防止幻读与序列化异常;
4、PostgreSQL 不支持 READ UNCOMMITTED,所有读操作天然规避脏读。
五、版本膨胀与 VACUUM 的协同关系
由于 MVCC 不就地更新,UPDATE 和 DELETE 操作持续产生新元组并保留旧元组,若不及时清理,将导致表文件持续增长、查询性能下降及磁盘空间耗尽。VACUUM 并非简单删除,而是识别并回收“对所有现存事务均不可见”的死亡元组所占空间。
1、VACUUM 不阻塞 DML,可在线执行,但仅回收空间,不重组物理页顺序;
2、VACUUM FULL 会重建整个表,释放空间并重排数据,但需排他锁,业务停写;
3、autovacuum 后台进程依据 pg_class.reltuples 与 pg_stat_all_tables.n_dead_tup 自动触发,是生产环境必需配置;
4、pg_visibility_map 和 FSM 文件辅助 VACUUM 快速定位可能含死亡元组的数据页。










