PostgreSQL Field Guide

PostgreSQL MVCC 与快照可见性

理解行版本、语句快照、长事务和 vacuum 之间的关系

MVCC(多版本并发控制)让普通读取通常不阻塞写入。UPDATE 不会就地覆盖所有读者看到的值,而会产生新行版本;每个查询按自己的 snapshot 判断哪个版本可见。

两个会话观察快照

先准备数据:

CREATE TABLE mvcc_demo (
  id integer PRIMARY KEY,
  value text NOT NULL
);
INSERT INTO mvcc_demo VALUES (1, 'before');

会话 A:

BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT value FROM mvcc_demo WHERE id = 1; -- before

会话 B:

UPDATE mvcc_demo SET value = 'after' WHERE id = 1;
COMMIT;

回到会话 A:

SELECT value FROM mvcc_demo WHERE id = 1; -- 仍是 before
COMMIT;
SELECT value FROM mvcc_demo WHERE id = 1; -- after

READ COMMITTED 则在每条语句开始时取得新 snapshot,所以同一事务里的第二次查询可能看到会话 B 已提交的值。

为什么长事务危险

只要旧 snapshot 仍可能看到某些行版本,vacuum 就不能把它们当作完全可回收。长事务因此会扩大:

  • dead tuples 与表/索引膨胀;
  • vacuum 工作量和磁盘占用;
  • 复制槽、逻辑解码或 standby 的保留压力;
  • transaction ID wraparound 风险窗口。

查找持有旧事务或 snapshot 的会话:

SELECT
  pid, usename, application_name, state,
  now() - xact_start AS transaction_age,
  age(backend_xmin) AS snapshot_xid_age,
  wait_event_type, wait_event,
  left(query, 120) AS query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL OR backend_xmin IS NOT NULL
ORDER BY xact_start NULLS LAST;

不要仅凭“时间长”终止会话;先确认业务、是否在执行备份/维护、事务能否安全重试及终止影响。

MVCC 不等于没有锁

行版本解决读取可见性,写写冲突、DDL、外键检查和显式锁仍会等待。下一步阅读锁等待与死锁

权威行为见 PostgreSQL 18 并发控制事务隔离

Last updated on

On this page