PostgreSQL通过多版本并发控制(MVCC)机制实现事务隔离,同一行数据会存在多个版本元组,每个元组都会携带事务相关的标记信息,事务读取数据时需要按照固定规则判断哪些元组对当前事务可见。

元组可见性判断的核心依据
PostgreSQL的每个数据元组(行)除了用户定义的字段外,还会包含多个系统隐藏字段,这些字段是判断元组可见性的核心依据:
- xmin:创建该元组的事务ID,即插入或更新该元组的事务标识
- xmax:删除或更新该元组的事务ID,若元组未被删除或更新则为空
- cmin/cmax:创建/删除该元组的事务内的命令ID,用于判断同一事务内不同命令的可见性
- ctid:元组的物理位置标识,不过可见性判断中不会直接使用该字段
事务的状态也会直接影响可见性判断,PostgreSQL的事务状态分为运行中、已提交、已回滚、进行中几种,判断时需要先获取相关事务的状态。
基础可见性判断规则
1. 插入元组的可见性判断
当判断一个由事务A创建的元组对当前事务B是否可见时,首先需要看xmin对应的事务A的状态:
- 如果事务A未提交且不是事务B本身,那么该元组对事务B不可见
- 如果事务A已提交,再判断事务B的开始时间是否晚于事务A的提交时间,若晚于则可见,否则不可见
- 如果事务A就是事务B本身,那么需要看cmin是否小于当前命令ID,小于则可见
2. 删除/更新元组的可见性判断
若元组的xmax不为空,说明该元组被事务C标记删除或更新,此时需要判断xmax对应的事务C的状态:
- 如果事务C未提交且不是事务B本身,那么该元组对事务B仍然可见
- 如果事务C已提交,那么该元组对事务B不可见,事务B会读取xmax对应的新版本元组
- 如果事务C就是事务B本身,那么需要看cmax是否小于当前命令ID,小于则不可见,否则可见
不同隔离级别下的规则差异
PostgreSQL支持读已提交和可重复读两种主要事务隔离级别,两者的元组可见性判断规则存在明显差异:
| 隔离级别 | 可见性判断特点 |
|---|---|
| 读已提交 | 事务每次执行查询时,都会重新获取当前已提交的事务快照,因此同一个事务内多次查询可能看到其他事务新提交的数据 |
| 可重复读 | 事务启动时获取一次事务快照,整个事务周期内都使用该快照判断可见性,不会看到事务启动后其他事务提交的新数据 |
可见性判断的示例验证
我们可以通过简单的实验来验证元组可见性规则,首先创建测试表并插入初始数据:
-- 创建测试表
CREATE TABLE test_visibility (
id INT,
name VARCHAR(20)
);
-- 插入初始数据,此时xmin为当前插入事务的ID
INSERT INTO test_visibility VALUES (1, 'test');
然后开启两个事务进行验证:
-- 事务A:更新数据但不提交 BEGIN; UPDATE test_visibility SET name = 'new_test' WHERE id = 1; -- 此时事务A内查询可以看到更新后的数据,xmax为事务A的ID -- 事务B:在事务A未提交时查询 BEGIN; SELECT * FROM test_visibility WHERE id = 1; -- 事务B看不到事务A的更新,因为事务A未提交,xmax对应的事务未提交,原元组仍然可见 COMMIT; -- 事务A提交后,事务B再次查询(读已提交级别下) BEGIN; SELECT * FROM test_visibility WHERE id = 1; -- 此时可以看到更新后的数据,因为事务A已提交,xmax对应的事务已提交 COMMIT;
特殊场景的可见性处理
还有一些特殊场景需要额外注意:
- 事务回滚的情况:如果xmin对应的事务已回滚,那么该元组对任何事务都不可见
- 子事务的场景:如果创建元组的是子事务,需要判断子事务的父事务状态,若父事务已提交则子事务的修改也视为已提交
- 系统事务ID:PostgreSQL的事务ID是循环使用的,判断时还需要考虑事务ID的回卷问题,避免旧事务的元组被误判为可见
元组可见性判断是PostgreSQL MVCC机制的核心,理解这些规则可以帮助开发者更好地排查事务并发场景下出现的读取不一致问题,也能更深入理解PostgreSQL的并发控制逻辑。
PostgreSQLMVCC元组可见性事务隔离级别修改时间:2026-07-01 07:45:26