PostgreSQL 事务与并发系列 · 第二期
Clyde Jin
04-24
DBA
31

PostgreSQL 事务与并发系列 · 第二期

深入 MVCC:可见性判断与事务快照揭秘

第一期我们建立了 ACID 和 MVCC 的基本概念。本期将掀开 MVCC 的引擎盖,剖析元组头部的 xmin/xmax、事务快照的内存结构,并通过实战案例看清“读已提交”与“可重复读”的本质差异。

一、回顾:MVCC 的核心思想

PostgreSQL 中,每一行数据(称为元组 tuple)在被修改时不会原地更新,而是插入一个新的行版本,旧的版本仍然留在数据页中。这些版本通过元组头部的两个事务 ID 字段来标记生命周期:

  • xmin – 插入该行版本的事务 ID
  • xmax – 删除或更新该行版本的事务 ID(0 表示未删除)

当事务开始时,系统会为其生成一个快照(Snapshot),里面记录了当前“哪些事务正在运行、哪些事务已经提交”等信息。随后,对于每一个可见性判断,PostgreSQL 会根据快照中的规则决定是否显示该行版本。


二、元组头中的秘密:xmin 与 xmax

查看一个普通表的元组头部信息,可以使用 pageinspect 扩展(需要超级用户权限):

CREATE EXTENSION pageinspect;

-- 创建一个测试表
CREATE TABLE test_mvcc (id int PRIMARY KEY, name text);
INSERT INTO test_mvcc VALUES (1, 'Alice'), (2, 'Bob');

-- 查看数据页的元组信息
SELECT lp, t_xmin, t_xmax, t_ctid FROM heap_page_items(get_raw_page('test_mvcc', 0));

输出示例:

lp t_xmin t_xmax t_ctid
1 730 0 (0,1)
2 730 0 (0,2)

这里 t_xmin=730 表示插入这两行的事务 ID 是 730,t_xmax=0 表示当前版本未被删除。

更新时的版本变化

-- 开启一个新事务(假设事务 ID 为 735)
BEGIN;
UPDATE test_mvcc SET name = 'Alice2' WHERE id = 1;

此时数据页内部会变成:

lp t_xmin t_xmax t_ctid
1 730 735 (0,3)
2 730 0 (0,2)
3 735 0 (0,3)
  • 旧版本(lp=1)的 t_xmax 被设置为 735,表示它已被事务 735 删除。
  • 新版本(lp=3)的 t_xmin = 735,表示它由事务 735 创建。
  • t_ctid 指向新版本的位置 (0,3),形成一条版本链。

当事务 735 提交后,旧版本对其他事务可能不可见,具体取决于隔离级别和快照。


三、事务快照(Snapshot)的内存布局

PostgreSQL 中,快照是一个结构体(SnapshotData),其核心成员包括:

  • xmin – 当前所有未提交事务中最小的 ID
  • xmax – 下一个将要被分配的事务 ID(即大于等于此值的所有事务,肯定未开始)
  • xip – 一个数组,存储所有活跃(未提交)的事务 ID
  • snapshot_type – 区分 SNAPSHOT_MVCC(普通快照)、SNAPSHOT_SELF(包含当前事务)等

快照是调用 GetSnapshotData() 函数生成的。简单流程:

  1. 记录当前系统活跃事务列表(从 ProcArray 中读取)
  2. 计算出最小的活跃事务 ID 作为 xmin
  3. nextXid(下一个未分配的事务 ID)作为 xmax
  4. 将活跃事务 ID 列表复制到 xip 数组

一个典型的快照内容(人为示例):

xmin = 100
xmax = 105
xip  = [101, 103]   (事务 102 已经提交,104 尚未开始)

四、可见性判断的核心规则

给定一个行版本,其 xmin 和 xmax,以及当前事务的快照,判断是否可见的简化逻辑如下:

对 xmin 的判断(该版本是否对当前事务可见?)

  1. 如果 xmin 是 已中止(aborted) 的事务 → 不可见(直接忽略)
  2. 如果 xmin 是 当前事务自己 且在事务未提交时 → 可见(自己的修改对自己可见)
  3. 如果 xmin 是 已提交 的事务:
    • 如果 xmin < 快照的 xmin → 可见
    • 如果 xmin >= 快照的 xmax → 不可见(该事务在快照生成之后才开始)
    • 如果 xmin 在快照的 xip 列表中(即活跃事务) → 不可见
    • 否则 → 可见

对 xmax 的判断(该版本是否已被删除/更新?)

  • 如果 xmax = 0 → 未被删除,通过 xmin 检查后即可见
  • 如果 xmax ≠ 0:
    • 如果 xmax 是已中止的事务 → 视为未删除
    • 如果 xmax 是当前事务自己 → 视为删除(如果当前事务删除了它,就不可见)
    • 如果 xmax 是已提交的事务且 xmax 在当前快照中可见 → 视为已删除 → 该版本不可见

核心结论:一个行版本可见的充要条件是 (xmin 有效且提交且不活跃) 并且 (xmax = 0 或 xmax 无效/未提交)

注意:上述规则是针对 普通 MVCC 快照SNAPSHOT_MVCC)的,也是 READ COMMITTEDREPEATABLE READ 隔离级别的基础。

五、读已提交 vs 可重复读:快照生成的差异

虽然两者都使用 MVCC 快照判断可见性,但快照的获取时机完全不同:

隔离级别 快照生成策略 效果
读已提交 事务中的每条 SQL 语句开始时,都会重新生成一个全新的快照 每条语句都能看到在它开始之前已经提交的所有数据
可重复读 事务中的第一条 SQL 语句开始时生成一个快照,整个事务期间复用该快照 事务中所有语句看到的是同一时刻的一致数据快照

实战案例:不可重复读现象

首先,创建一个表并插入初始数据:

CREATE TABLE accounts (id INT PRIMARY KEY, balance INT);
INSERT INTO accounts VALUES (1, 100);

场景演示(读已提交)

时间 事务 A(读已提交) 事务 B(读已提交)
T1 BEGIN; BEGIN;
T2 SELECT balance FROM accounts WHERE id=1; → 100
T3 UPDATE accounts SET balance=200 WHERE id=1; COMMIT;
T4 SELECT balance FROM accounts WHERE id=1; → 200
T5 COMMIT;

事务 A 在同一次事务内的两次查询结果不同(100 → 200),这就是“不可重复读”。

场景演示(可重复读)

时间 事务 A(可重复读) 事务 B(可重复读)
T1 BEGIN; BEGIN;
T2 SELECT balance FROM accounts WHERE id=1; → 100
T3 UPDATE accounts SET balance=200 WHERE id=1; COMMIT;
T4 SELECT balance FROM accounts WHERE id=1; → 仍为 100
T5 COMMIT;

事务 A 在整个事务期间看到的是同一个快照(T2 时刻的快照),因此第二次查询仍然返回 100,实现了“可重复读”。


六、实践:使用 pg_snapshot_xip 观察快照

PostgreSQL 提供 pg_current_snapshot() 函数,返回当前会话的快照文本表示,格式为 xmin:xmax:xip_list

-- 开启一个可重复读事务
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT pg_current_snapshot();
-- 输出示例: 720:724:720,722

-- 再开另一个会话,执行一些更新并提交
-- 回到原会话,再次获取快照
SELECT pg_current_snapshot();
-- 输出相同:720:724:720,722

-- 在读已提交事务中,每次查询前快照会变化
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT pg_current_snapshot();
-- 第一次
SELECT pg_current_snapshot();
-- 第二次(可能在另一个事务提交后)会看到不同内容
COMMIT;

通过这种方式可以直观感受快照的生命周期差异。


七、长事务与快照膨胀的风险

当一个事务以 可重复读可串行化 隔离级别运行很长时间时,它会一直持有一个旧的快照。这会导致:

  1. 表膨胀:所有在快照生成之后被更新/删除的旧版本,都不能被 VACUUM 清理,因为旧快照仍然需要看见它们。
  2. 事务 ID 回卷风险:长时间持有的快照可能会阻止 xid 冻结,导致数据库强制关闭。

监控长事务

SELECT pid, age(backend_xid) AS xid_age, 
       backend_xmin, state, query_start
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL
ORDER BY age(backend_xid) DESC;

若发现长事务,应及时评估是否可以终止(pg_terminate_backend(pid))或让其结束。


八、总结与下期预告

本期我们完成了对 MVCC 机制的深度探索:

  • 元组头部 xmin / xmax 如何刻画行版本的生命周期
  • 事务快照的内存结构与生成原理
  • 可见性判断的核心逻辑(结合快照判断)
  • READ COMMITTEDREPEATABLE READ 的本质差异(快照获取时机不同)
  • 长事务带来的膨胀与事务 ID 回卷风险

这些知识是分析 PostgreSQL 并发问题的“透视镜”。

第三期预告:锁机制与死锁处理实战

我们将分析 PostgreSQL 的表级锁(ACCESS SHARE、ROW EXCLUSIVE、ACCESS EXCLUSIVE 等)、行级锁(FOR UPDATE、FOR NO KEY UPDATE…),以及死锁的检测与预防。同时会给出常用的排查脚本,帮助你在线解决“卡住的事务”问题。

敬请期待第三期!

如果你在使用 PSQL 过程中遇到任何与 MVCC 相关的困惑(比如明明更新了数据却查不到、表膨胀严重),欢迎在评论区交流。下期见!

Star
Donate
PostgreSQL 事务与并发系列 · 第一期
Previous
PostgreSQL 事务与并发系列 · 第三期
Next

Leave a comment

Registration is not required

Clyde Jin
302 Articles
0 Comments
0 Like
Recent Posts

HiddenMerit Daily · Issue 59

📊 HiddenMerit Daily · Issue 59 Focus on Database Frontiers, Practical Insights for DBAs July 21, 2026 | 5 Selected Global Breaking News 01|Oracle Issues Urgent Warning: July 21 Release Update to Fix Large Number of High‑Risk Vulnerabilities, Immediate Deployment Recommended On July 13, Oracle issued an urgent security warning, strongly recommending that all customers running supported Oracle Database versions (including Oracle Database 19c and Oracle AI Database 26ai) immediately test and deploy the Release Update (RU) after its release on July 21. Background: New frontier AI models are significantly lowering the barrier to discovering and exploiting software vulnerabilities – these models can identify weaknesses, analyse software changes, reverse‑engineer security patches, and develop potential attack paths at unprecedented speed and scale. AI models are also becoming increasingly adept at combining multiple weaknesses across the application and data stack into complex attacks, even when individual weaknesses do not themselves pose a serious risk. As a result, protecting systems solely at the network or application layer is no longer sufficient; enterprises must protect the entire technology stack. Fixes Included in This RU: Oracle has collaborated with state‑of‑the‑art models from Anthropic and OpenAI to proactively identify and fix potential security vulnerabilities. The upcoming […]

HiddenMerit Daily · Issue 58

📊 HiddenMerit Daily · Issue 58 Focus on Database Frontiers, Practical Insights for DBAs July 20, 2026 | 5 Selected Global Breaking News 01|CAICT: Domestic Databases Enter Core System “Deep Water,” AI‑Native Leads New Industry Landscape On July 9, at the 2026 Trustworthy Database Development Conference, CAICT released the “Database Development Research Report (2026).” The report notes that domestic databases have basically completed peripheral system replacement and have officially entered the critical business system breakthrough phase. Key Data: The global database market reached $131.6 billion in 2025 (approximately RMB 894.09 billion). The Chinese database market reached $9.49 billion in 2025 (approximately RMB 64.48 billion), and is expected to reach RMB 97.974 billion by 2028, with a CAGR of 13.06%. The number of domestic database vendors has shrunk from a peak of 167 to 94, with a clear head‑concentration effect and an intensifying “Matthew effect.” AI‑Native Becomes the Main Theme: The report points out that database technology is accelerating its evolution toward the AI‑native direction, and the global database industry is entering a new phase of landscape restructuring. The role of databases is upgrading from “underlying support systems” to “core engines enabling intelligent decision‑making and business innovation.” AI agents are becoming […]

HiddenMerit Daily · Issue 56

📊 HiddenMerit Daily · Issue 56 Focus on Database Frontiers, Practical Insights for DBAs July 6, 2026 | 5 Selected Global Breaking News 01|Kingware Releases Manufacturing Scenario Database Evolution White Paper: SQL Server Replacement Enters “Deep Water” On July 5, CETC Kingware published a technical article titled “Database Evolution in Manufacturing Scenarios: How Kingware Replaces SQL Server,” pointing out that the data surge on industrial shop floors has exceeded the processing capacity of single‑node systems. Traditional database architectures that rely on vertical scaling are facing unprecedented performance challenges. Over the next 1‑3 years, manufacturing enterprises will no longer face only the “choice of replacement,” but must answer the strategic question: “How do we build an autonomous data foundation in the context of de‑IOE?” Three Paradigm Shifts: Hybrid Workloads Become the Norm: The IT architecture of modern factories is shifting from separated OLTP and OLAP to HTAP mode. The same data system must simultaneously handle tens of thousands of device instruction writes per second and minute‑level production report analysis. Distributed Architecture Becomes a Hard Requirement: When a single table exceeds 100 million rows with daily increments exceeding 1 million rows, the index maintenance cost of traditional single‑node databases rises exponentially. Autonomous […]

HiddenMerit Daily · Issue 57

📊 HiddenMerit Daily · Issue 57 Focus on Database Frontiers, Practical Insights for DBAs July 17, 2026 | 5 Selected Global Breaking News 01|CAICT Releases “Database Development Research Report (2026)”: China Market to Reach RMB 98 Billion by 2028, AI‑Native Becomes the Main Theme On July 9, at the 2026 Trustworthy Database Development Conference, the China Academy of Information and Communications Technology (CAICT) officially released the “Database Development Research Report (2026),” comprehensively revealing the latest landscape and evolution directions of the global and Chinese database markets. Key Data: Global Market: The global database market reached $131.6 billion in 2025 (approximately RMB 894.09 billion). Chinese Market: The Chinese database market reached $9.49 billion in 2025 (approximately RMB 64.48 billion), accounting for 7.2% of the global market. By 2028, the total Chinese database market is expected to reach RMB 97.974 billion, with a compound annual growth rate of 13.06% . Industry Landscape: There are 394 database product providers globally, with China and the US leading in vendor count. The number of global open‑source and closed‑source products is roughly balanced. Commercial databases account for over 70% in China, while the proportion of open‑source databases is increasing. After rapid growth from 2022 to 2024, […]
生成中...
扫描二维码
扫描二维码