Skip to content

MVCC 与并发控制

快照隔离、xmin/xmax 版本链、读不阻塞写、锁(行锁/表锁/咨询锁)、与 MySQL 的对比。

Updated View as Markdown
For humans

MVCC 与并发控制

PostgreSQL 的并发控制核心是快照隔离:每个事务看到的是某个时间点的数据库快照,读写互不阻塞。官方表述是“读不阻塞写,写不阻塞读”,这是 PG 并发性能的根基,也和 MySQL 的 MVCC 有本质差异。

版本链:xmin/xmax

PG 没有 undo log,旧版本直接留在堆表里。每个元组头部带:

  • xmin:插入这个版本的事务 ID
  • xmax:删除或锁定这个版本的事务 ID
  • ctid:指向下一个版本的指针(HOT 链)

UPDATE 插入新版本,旧版本标记 xmax 后留在页里;DELETE 只标记 xmax。可见性判断 = 版本链上的 xmin/xmax 与当前事务快照的比较,配合 CLOG(事务提交状态)。

快照

每个事务(或每条语句,取决于隔离级别)生成快照,快照记录当时活跃事务的边界。元组可见性规则:

  • xmin 已提交且在快照之前 → 可见
  • xmin 活跃(未提交)→ 不可见
  • xmax 已提交且在快照之前 → 已删除,不可见

关键差异:MySQL 的旧版本在 undo log 里,靠回滚指针回溯;PG 的旧版本在数据页里,靠版本链和 VACUUM 管理。所以 PG 的 UPDATE 会产生死元组,表会膨胀,需要 Vacuum(见 Vacuum 篇)。

并发控制手段

机制 用途
快照隔离(MVCC) 普通读不加锁,读写互不阻塞
行锁 UPDATE/DELETE 锁行,通过元组 xmax 和 MultiXact 实现
表锁 DDL、ACCESS EXCLUSIVE 等
咨询锁(advisory lock) 应用层锁,可模拟分布式锁

行锁与 MySQL 的差异:PG 没有间隙锁和临键锁(锁的是元组不是索引范围),这决定了隔离级别的行为差异(见事务篇)。死锁检测靠等待图,报错 40001,事务自动回滚。

与 MySQL MVCC 的对比

维度 PostgreSQL MySQL InnoDB
旧版本存放 堆表内版本链 undo log
清理机制 VACUUM purge 线程
可见性依据 xmin/xmax + CLOG 隐藏字段 + Read View
读锁 不加锁 不加锁(快照读)
行锁范围 只锁元组 记录锁 + 间隙锁 + 临键锁

面试追问

  1. PG 的 MVCC 和 MySQL 有什么区别? 旧版本一个在堆里一个在 undo log。PG 靠 VACUUM 清理死元组,MySQL 靠 purge 线程
  2. 读会阻塞写吗? 不会。普通读走快照,不获取任何锁,写者创建新版本。这是快照隔离的核心
  3. xmin/xmax 是什么? 插入和删除该版本的事务 ID。版本链加 CLOG 判断可见性
  4. 咨询锁是什么? 应用层显式获取的锁,不绑定数据行,可模拟分布式锁
  5. PG 为什么没有间隙锁? 锁基于元组而非索引范围,配合 SSI(可串行化快照隔离)处理并发冲突,机制与 MySQL 的临键锁不同
Navigation

Type to search…

↑↓ navigate↵ selectEsc close