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

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

事务隔离级别深度测试与序列化异常

前三期我们构建了从 MVCC 到锁机制的完整知识体系。本期将聚焦三种隔离级别的实际行为边界,通过可复现的案例演示“不可重复读”“幻读”以及最隐蔽的“写偏斜(Write Skew)”,同时揭开 PostgreSQL 可串行化快照隔离(SSI)的神秘面纱。

一、隔离级别速览与默认行为回顾

PostgreSQL 支持三种隔离级别(READ UNCOMMITTED 实际等同于 READ COMMITTED):

隔离级别 脏读 不可重复读 幻读 序列化异常(写偏斜)
READ COMMITTED ✅ 可能 ✅ 可能 ✅ 可能
REPEATABLE READ ❌(PostgreSQL 实现彻底阻止) ✅ 可能
SERIALIZABLE ❌(检测冲突并终止事务)

关键事实:PostgreSQL 的 REPEATABLE READ 级别不仅阻止了不可重复读,还彻底阻止了幻读(通过快照隔离实现了比 SQL 标准更严格的语义)。但即便如此,仍无法防止写偏斜这种序列化异常。


二、实战环境准备

创建一个简单的医生值班表,用于演示所有异常场景:

DROP TABLE IF EXISTS doctors;
CREATE TABLE doctors (
    id   INT PRIMARY KEY,
    name TEXT,
    on_call BOOLEAN        -- 是否值班
);

INSERT INTO doctors VALUES (1, 'Alice', true), (2, 'Bob', true);

规则:至少有一名医生值班(业务约束)。两个事务同时试图交接班,可能会导致违反约束。


三、READ COMMITTED:不可重复读与幻读

3.1 不可重复读

时间 事务 A (READ COMMITTED) 事务 B (READ COMMITTED)
T1 BEGIN;
T2 SELECT on_call FROM doctors WHERE id=1; → true
T3 UPDATE doctors SET on_call=false WHERE id=1; COMMIT;
T4 SELECT on_call FROM doctors WHERE id=1;false
T5 COMMIT;

同一个事务内读取到不同的值 → 不可重复读

3.2 幻读

在 PostgreSQL 中,幻读通常指“范围内新增/删除的行导致两次查询结果集不同”。虽然 REPEATABLE READ 能阻止,但 READ COMMITTED 仍会出现:

时间 事务 A 事务 B
T1 BEGIN;
T2 SELECT COUNT(*) FROM doctors WHERE on_call=true; → 2
T3 INSERT INTO doctors VALUES (3,'Carol',true); COMMIT;
T4 SELECT COUNT(*) FROM doctors WHERE on_call=true;3

出现了之前未见的行 → 幻读


四、REPEATABLE READ:快照隔离如何阻止幻读

在 REPEATABLE READ 隔离级别下,事务在第一条语句执行时获取一个固定的快照,整个事务期间复用。因此 T4 查询仍然看到的是 T2 时刻的快照结果,不会受 T3 提交的影响。

验证脚本

-- 会话 1
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT COUNT(*) FROM doctors WHERE on_call=true;  -- 假设为 2

-- 会话 2
BEGIN;
INSERT INTO doctors VALUES (3,'Carol',true);
COMMIT;

-- 会话 1 再次执行
SELECT COUNT(*) FROM doctors WHERE on_call=true;  -- 仍然为 2(快照未变)
COMMIT;

但请注意:REPEATABLE READ 并不会阻止序列化异常中的写偏斜,下面来看。


五、写偏斜(Write Skew)——最隐蔽的并发异常

5.1 什么是写偏斜?

两个并发事务都读取一组数据,然后各自基于读取的结果修改互不相交的数据子集,最终导致整体违反某个业务约束。它不会产生冲突的行锁,因此 MVCC 快照隔离无法自动防止。

5.2 经典医生值班示例

业务约束:至少有一名医生需要值班on_call = true 的医生数量 ≥ 1)。

初始状态:Alice 和 Bob 均为值班(true)。

现在两个事务同时试图将 Alice 和 Bob 分别设为不值班:

时间 事务 1 (REPEATABLE READ) 事务 2 (REPEATABLE READ)
T1 BEGIN; BEGIN;
T2 SELECT COUNT(*) FROM doctors WHERE on_call=true; → 2 SELECT COUNT(*) FROM doctors WHERE on_call=true; → 2
T3 判断 2>=1,允许交接班 判断 2>=1,允许交接班
T4 UPDATE doctors SET on_call=false WHERE id=1; UPDATE doctors SET on_call=false WHERE id=2;
T5 COMMIT; COMMIT;

最终结果:两名医生都不值班 → 违反约束

为什么 PostgreSQL 的快照隔离没有阻止?因为:

  • 事务 1 和事务 2 锁定的行分别是 id=1 和 id=2,没有交集。
  • 每个事务的写操作没有与对方的写操作冲突,因此可以并发提交。
  • 只在提交后检查约束才发现违反(但 PostgreSQL 默认不自动检测这种逻辑上的违反)。

5.3 如何在 REPEATABLE READ 中强制避免写偏斜?

方法 1:主动加锁保护整个约束集(例如使用 FOR UPDATE 锁定所有相关行)

BEGIN ISOLATION LEVEL REPEATABLE READ;
-- 锁定所有可能被修改的相关行(这里是所有医生行)
SELECT * FROM doctors WHERE on_call = true FOR UPDATE;
-- 现在业务逻辑判断,然后更新
UPDATE doctors SET on_call=false WHERE id=1;
COMMIT;

因为两个事务都会尝试锁定所有 on_call=true 的行,第二个事务会被第一个阻塞,串行执行,从而避免写偏斜。

方法 2:使用 SERIALIZABLE 隔离级别(见下一节)。


六、SERIALIZABLE:可串行化快照隔离(SSI)

6.1 SSI 原理简介

PostgreSQL 的 SERIALIZABLE 隔离级别基于 可串行化快照隔离(Serializable Snapshot Isolation,SSI) 算法。它在后台跟踪事务之间的读写依赖(rw-conflict),如果检测到依赖图中出现了循环(可能导致类似写偏斜的异常),就会主动终止其中一个事务,强制回滚。

优点:用户无需手动加锁,只要设置 SERIALIZABLE,数据库自动检测序列化冲突。

缺点:事务可能因序列化失败而被回滚,应用需要重试。

6.2 写偏斜的 SERIALIZABLE 行为

将上面的医生值班示例隔离级别改为 SERIALIZABLE

时间 事务 1 (SERIALIZABLE) 事务 2 (SERIALIZABLE)
T1 BEGIN ISOLATION LEVEL SERIALIZABLE; BEGIN ISOLATION LEVEL SERIALIZABLE;
T2 SELECT COUNT(*) FROM doctors WHERE on_call=true; → 2 SELECT COUNT(*) FROM doctors WHERE on_call=true; → 2
T3 判断通过 判断通过
T4 UPDATE doctors SET on_call=false WHERE id=1; UPDATE doctors SET on_call=false WHERE id=2;
T5 COMMIT; COMMIT;其中一个会收到错误ERROR: could not serialize access due to read/write dependencies among transactions

PostgreSQL 检测到两个事务的写集合交集为空但读集合重叠(都读取了 on_call=true 的行),并且存在读写依赖循环,于是终止其中一个。应用捕获异常后重试即可。

6.3 SERIALIZABLE 与性能权衡

  • 优点:最强的数据一致性保证,防止所有序列化异常(包括写偏斜)。
  • 缺点:高冲突负载下,事务回滚率上升,需要重试逻辑;SSI 的检测本身也有一定开销。

建议:只有当业务约束确实要求“可串行化”时(例如金融系统中防止余额超扣等),才使用 SERIALIZABLE。对于大多数 Web 应用,REPEATABLE READ + 适当的乐观锁/悲观锁(如版本号或 FOR UPDATE)足够。


七、如何选择隔离级别?

场景 推荐隔离级别 理由
普通报表、简单查询 READ COMMITTED 性能好,无需额外开销
同一事务内多次查询需保持一致(例如生成报告期间表不变) REPEATABLE READ 快照一致,无幻读
任务队列、行级竞争极低 READ COMMITTED 足够
库存扣减、转账(两个账户余额总和不变) REPEATABLE READ + FOR UPDATE 显式行锁避免写偏斜
复杂的多表多行业务约束(如排班、预订系统等) SERIALIZABLE + 重试 让数据库帮你检测冲突,简化代码
高并发下对性能要求极高,且允许极低概率的写偏斜 REPEATABLE READ 但需要充分理解风险

八、可串行化应用的重试模式

在使用 SERIALIZABLE 时,必须准备重试逻辑(伪代码):

def transfer_money():
    while True:
        try:
            with db.transaction(isolation='SERIALIZABLE'):
                # 读取余额,检查约束
                # 执行更新
                db.commit()
                break
        except SerializableError:
            # 自动重试
            continue

PostgreSQL 返回的序列化失败错误码为 40001(SQLSTATE),应用应据此重试。


九、总结与下期预告

本期我们完成了对三种隔离级别的深度剖析:

  • READ COMMITTED:每条语句新快照,允许不可重复读和幻读。
  • REPEATABLE READ:事务级固定快照,阻止不可重复读与幻读,但不能防止写偏斜。
  • SERIALIZABLE:基于 SSI,自动检测读写依赖,防止所有序列化异常,但需重试。
  • 通过医生值班案例,复现了写偏斜并演示了 SERIALIZABLE 如何自动回滚。

第五期预告:实战调优——事务的常见陷阱与最佳实践

  • 长事务与表膨胀的治理方案
  • idle in transaction 的危害与自动清理
  • 高并发下如何设置 deadlock_timeoutlock_timeout
  • 如何利用 pg_stat_activity 建立实时告警
  • 事务与连接池的配合(避免事务悬挂)

第五期将帮助你把这些理论知识落地到生产环境的日常监控与优化中,敬请期待!

有任何关于隔离级别、序列化异常的疑问或实战踩坑经历,欢迎在评论区讨论,下期见!

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

Leave a comment

Registration is not required

Clyde Jin
303 Articles
0 Comments
0 Like
Recent Posts

HiddenMerit Daily · Issue 60

📊 HiddenMerit Daily · Issue 60 Focus on Database Frontiers, Practical Insights for DBAs July 22, 2026 | 5 Selected Global Breaking News 01|Oracle Releases Largest Quarterly Patch in History: 1,449 Patches Fix 1,235 CVEs, 261 Critical On July 21, Oracle released its July 2026 Critical Patch Update (CPU), setting a record for the largest single patch release in the company’s history. This CPU contains 1,449 security patches fixing 1,235 independent CVEs across 32 Oracle product families, of which 261 patches are rated Critical. Patch Distribution by Product Family: Product Family Patches Remotely Exploitable Without Authentication Oracle E‑Business Suite 410 45 Oracle Fusion Middleware 355 219 Oracle Communications 168 122 Oracle MySQL 54 9 Oracle Database Server 15 6 Context for This Patch: Oracle had already issued an urgent “Prepare Now” warning a week earlier, emphasising that AI is fundamentally lowering the barrier to discovering and exploiting vulnerabilities – frontier AI models can analyse software changes, reverse‑engineer security patches, and develop attack paths at unprecedented speed. Oracle has collaborated with state‑of‑the‑art models from Anthropic and OpenAI to proactively identify and fix potential security vulnerabilities. Oracle strongly recommends that customers immediately test and deploy this update in their monthly patch cycles. […]

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 […]
生成中...
扫描二维码
扫描二维码