news 2026/4/18 12:47:12

PostgreSQL 核心原理:读已提交与可重复读的底层实现差异(事务隔离级别)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL 核心原理:读已提交与可重复读的底层实现差异(事务隔离级别)

文章目录

    • 一、事务隔离级别概述:SQL 标准 vs PostgreSQL 实现
      • 1.1 SQL 标准定义的隔离级别问题
      • 1.2 关键差异对比表
      • 1.3 实践建议
    • 二、核心机制基础:MVCC 与事务快照(Snapshot)
      • 2.1 快照(Snapshot)是什么?
      • 2.2 可见性判断
    • 三、读已提交(READ COMMITTED):语句级快照
      • 3.1 快照获取时机
      • 3.2 行为示例
      • 3.3 底层实现细节
      • 3.4 优点与代价
    • 四、可重复读(REPEATABLE READ):事务级快照 + SSI 冲突检测
      • 4.1 快照获取时机
      • 4.2 行为示例
      • 4.3 底层实现:SSI(Serializable Snapshot Isolation)
      • 4.4 为什么 RR 能禁止幻读?
    • 五、监控与诊断
      • 5.1 查看当前事务隔离级别
      • 5.2 检测长 RR/SERIALIZABLE 事务
      • 5.3 监控 SSI 冲突(仅 SERIALIZABLE)
    • 六、底层代码路径简析(PostgreSQL 15+)
      • 6.1 快照获取逻辑(`src/backend/utils/time/snapmgr.c`)
      • 6.2 SSI 初始化(`src/backend/storage/lmgr/procarray.c`)
      • 6.3 可见性判断(`src/backend/access/heap/heapam_visibility.c`)
    • 七、常见陷阱与问题解决
      • 7.1 陷阱 1:误以为 RR 能避免所有并发问题
      • 7.2 陷阱 2:长事务导致膨胀

PostgreSQL 作为一款高度可靠的开源关系型数据库,其事务隔离机制是保障数据一致性和并发性能的核心支柱。在 SQL 标准定义的四种隔离级别中,读已提交(Read Committed)可重复读(Repeatable Read)是最常被使用的两种。尽管它们名称相似,但在 PostgreSQL 中的底层实现机制却存在根本性差异——这种差异直接影响了应用的行为、性能表现和一致性保证。

本文将深入 PostgreSQL 内核,从快照(Snapshot)获取时机、可见性判断逻辑、冲突检测机制等维度,全面剖析READ COMMITTEDREPEATABLE READ的实现原理,并揭示 PostgreSQL 如何通过MVCC + SSI(Serializable Snapshot Isolation)技术,在不牺牲性能的前提下提供强一致性保障。


一、事务隔离级别概述:SQL 标准 vs PostgreSQL 实现

PostgreSQL 的READ COMMITTEDREPEATABLE READ虽然只有一字之差,但其底层实现体现了两种不同的并发哲学:

  • READ COMMITTED:追求高吞吐与低延迟,接受“语句间视图漂移”,适合大多数 OLTP 场景。
  • REPEATABLE READ:追求事务内一致性,通过事务级快照 + SSI 依赖跟踪,不仅禁止不可重复读,还意外地禁止了幻读,成为 PostgreSQL 的“隐藏王牌”。

而这一切的背后,是 MVCC 与 SSI 的精妙结合——既避免了传统锁模型的阻塞开销,又提供了远超 SQL 标准的一致性保障。

记住:在 PostgreSQL 中,选择隔离级别不仅是选择“一致性强度”,更是选择“并发模型”。理解其底层机制,才能写出既正确又高效的数据库应用。

1.1 SQL 标准定义的隔离级别问题

隔离级别脏读不可重复读幻读
Read Uncommitted✅ 允许✅ 允许✅ 允许
Read Committed❌ 禁止✅ 允许✅ 允许
Repeatable Read❌ 禁止❌ 禁止✅ 允许
Serializable❌ 禁止❌ 禁止❌ 禁止

注:✅ 表示“可能发生”,❌ 表示“被禁止”

然而,PostgreSQL 并未完全遵循这一标准

  • 不支持READ UNCOMMITTED:最低级别即为READ COMMITTED
  • REPEATABLE READ实际禁止幻读:行为上等同于标准的SERIALIZABLE
  • 真正的SERIALIZABLE使用 SSI 算法,可能回滚事务以保证串行化

这种“超规格”实现,正是 PostgreSQL 并发控制先进性的体现。

1.2 关键差异对比表

特性READ COMMITTEDREPEATABLE READ
快照粒度每条 SQL 语句整个事务
不可重复读允许禁止
幻读允许禁止(PostgreSQL 特性)
SSI 依赖跟踪
冲突检测否(但记录依赖)
性能开销极低略高(需维护依赖图)
适用场景Web 应用、日志系统金融交易、报表统计
首次访问触发每次语句事务中第一次读/写

1.3 实践建议

场景推荐隔离级别理由
普通 Web 查询READ COMMITTED性能最优,足够安全
财务对账、报表REPEATABLE READ保证数据一致性
高并发金融交易SERIALIZABLE防止写偏斜,强一致性
批量数据导入READ COMMITTED减少快照开销

二、核心机制基础:MVCC 与事务快照(Snapshot)

要理解隔离级别的差异,必须先掌握 PostgreSQL 的MVCC(多版本并发控制)事务快照(Snapshot)机制。

2.1 快照(Snapshot)是什么?

快照是一个数据结构(SnapshotData),定义了当前事务“能看到哪些数据”。它包含三个关键字段:

TransactionId xmin;// 所有 < xmin 的事务已结束(提交或回滚)TransactionId xmax;// 所有 >= xmax 的事务尚未开始TransactionId*xip;// 当前活跃事务 ID 列表(未提交)

快照的本质是一个时间窗口:只有在此窗口“之前”已提交的修改才可见。

2.2 可见性判断

PostgreSQL 通过元组头中的t_xmin(创建事务)和t_xmax(删除/更新事务)结合快照,判断某条记录是否对当前事务可见。这是所有隔离级别的共同基础。


三、读已提交(READ COMMITTED):语句级快照

3.1 快照获取时机

  • 每次执行 SQL 语句时,重新获取一个新的快照
  • 即使在同一事务中,两次SELECT也可能看到不同结果

3.2 行为示例

-- 会话 ABEGIN;SELECTbalanceFROMaccountsWHEREid=1;-- 返回 100-- 此时会话 B 执行:-- UPDATE accounts SET balance = 200 WHERE id = 1; COMMIT;-- 会话 A 继续:SELECTbalanceFROMaccountsWHEREid=1;-- 返回 200!COMMIT;

✅ 第二次查询看到了会话 B 已提交的修改
⚠️ 这就是“不可重复读”——被 SQL 标准允许,但在某些业务场景中是危险的

3.3 底层实现细节

  • 每次调用ExecutorStart()(执行器启动)时,若当前快照为空,则调用GetSnapshotData()获取新快照
  • 对于UPDATE/DELETE,目标行的可见性判断使用当前语句快照,但写入的新元组t_xmin为当前事务 ID
  • 写操作不会阻塞读,因为读的是旧版本

3.4 优点与代价

优点代价
并发度高,响应快同一事务内数据视图不一致
无额外冲突检测开销不适用于需要强一致性的场景(如转账)

四、可重复读(REPEATABLE READ):事务级快照 + SSI 冲突检测

4.1 快照获取时机

  • 事务首次访问数据时(通常是第一条 SQL 执行时)获取一次快照
  • 整个事务生命周期复用该快照
  • 所有查询看到完全一致的数据视图

4.2 行为示例

-- 会话 ABEGINTRANSACTIONISOLATIONLEVELREPEATABLEREAD;SELECTbalanceFROMaccountsWHEREid=1;-- 返回 100-- 会话 B:-- UPDATE accounts SET balance = 200 WHERE id = 1; COMMIT;-- 会话 A 继续:SELECTbalanceFROMaccountsWHEREid=1;-- 仍返回 100!COMMIT;

✅ 两次查询结果一致 →禁止不可重复读
✅ 即使会话 B 插入新行,会话 A 的SELECT COUNT(*)也不会变化 →禁止幻读

📌 这是 PostgreSQL 对 SQL 标准的“增强”:RR 级别实际达到了 Serializable 的效果(除极少数情况)

4.3 底层实现:SSI(Serializable Snapshot Isolation)

从 PostgreSQL 9.1 开始,REPEATABLE READSERIALIZABLE都基于SSI 算法实现,区别仅在于是否启用冲突检测

隔离级别快照类型是否记录读写依赖是否检测冲突冲突时行为
READ COMMITTED语句级
REPEATABLE READ事务级允许提交
SERIALIZABLE事务级回滚事务

SSI 的核心思想:

  1. 记录“危险结构”(Dangerous Structures)

    • 事务 A 读取某行
    • 事务 B 修改该行并提交
    • 事务 A 后续又写入相关数据
      → 形成“读-写-写”依赖链,可能导致非串行化结果
  2. 构建序列化图(Serialization Graph)

  3. 检测环(Cycle):若有环,则存在不可串行化调度

REPEATABLE READ下,PostgreSQL记录依赖但不检测环,因此不会回滚,但能防止幻读;
SERIALIZABLE下,检测环并回滚,提供严格串行化。

4.4 为什么 RR 能禁止幻读?

传统数据库通过范围锁(Range Lock)防止幻读,但会严重降低并发。

PostgreSQL 的做法更巧妙:

  • 由于使用事务级快照,所有查询都基于同一时间点
  • 即使其他事务插入新行,只要其t_xmin >= snapshot.xmax,就不可见
  • 对于UPDATE/DELETE影响“未来行”的情况,SSI 会跟踪谓词(predicate)依赖

例如:

-- 事务 A (RR)SELECT*FROMordersWHEREstatus='pending';-- 返回 0 行-- 事务 BINSERTINTOorders(status)VALUES('pending');COMMIT;-- 事务 AUPDATEordersSETpriority=1WHEREstatus='pending';-- 影响 0 行

即使事务 B 插入了匹配行,事务 A 的UPDATE也不会影响它——因为该行在快照中不可见。这本质上消除了幻读。


五、监控与诊断

5.1 查看当前事务隔离级别

SHOWtransaction_isolation;-- 或SELECTcurrent_setting('transaction_isolation');

5.2 检测长 RR/SERIALIZABLE 事务

SELECTpid,query,xact_start,now()-xact_startASxact_age,wait_event_type,wait_eventFROMpg_stat_activityWHEREstate<>'idle'AND(current_queryLIKE'%REPEATABLE READ%'ORcurrent_queryLIKE'%SERIALIZABLE%')ORDERBYxact_ageDESC;

5.3 监控 SSI 冲突(仅 SERIALIZABLE)

SELECT*FROMpg_stat_database_conflictsWHEREdatname=current_database();-- 查看 serialization_failures

六、底层代码路径简析(PostgreSQL 15+)

6.1 快照获取逻辑(src/backend/utils/time/snapmgr.c

SnapshotGetTransactionSnapshot(void){if(IsolationUsesXactSnapshot())returnGetSnapshotData(&CurrentSnapshotData);elsereturnGetLatestSnapshot();// READ COMMITTED 走这里}
  • IsolationUsesXactSnapshot()返回 true 当且仅当隔离级别 ≥REPEATABLE READ

6.2 SSI 初始化(src/backend/storage/lmgr/procarray.c

if(XactIsoLevel==XACT_REPEATABLE_READ||XactIsoLevel==XACT_SERIALIZABLE){SISetup();// 初始化 SSI 结构}

6.3 可见性判断(src/backend/access/heap/heapam_visibility.c

无论哪种隔离级别,最终都调用HeapTupleSatisfiesMVCC(),但传入的快照不同。


七、常见陷阱与问题解决

7.1 陷阱 1:误以为 RR 能避免所有并发问题

虽然 RR 禁止不可重复读和幻读,但仍可能发生写偏斜(Write Skew)

-- 假设库存表:product_id, stock-- 业务规则:总库存 >= 0-- 事务 A (RR)SELECTstockFROMinventoryWHEREproduct_id=1;-- 5SELECTstockFROMinventoryWHEREproduct_id=2;-- 5-- 事务 B (RR)SELECTstockFROMinventoryWHEREproduct_id=1;-- 5SELECTstockFROMinventoryWHEREproduct_id=2;-- 5-- A: UPDATE inventory SET stock = 0 WHERE product_id = 1;-- B: UPDATE inventory SET stock = 0 WHERE product_id = 2;-- 结果:总库存 = 0,看似合法-- 但如果业务要求“任一产品库存不能低于 3”,则违反规则!

✅ 解决方案:使用SERIALIZABLE隔离级别,SSI 会检测到此冲突并回滚其中一个事务。

7.2 陷阱 2:长事务导致膨胀

RR 事务持有快照时间越长,阻止 VACUUM 清理的死元组越多,表膨胀风险越高。

建议:RR 事务应尽量短;设置idle_in_transaction_session_timeout

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 10:05:48

交互装置在2026展厅展馆中的常见展项

在当代展厅和展馆中&#xff0c;交互装置已经成为不可或缺的重要组成部分。这些装置不仅提升了观众的参观体验&#xff0c;还增强了展览内容的传达效果和记忆点。从多种多样的互动展项中&#xff0c;我们可以看到交互装置在展厅展馆中的多方面重要性。 交互装置极大地提升了观众…

作者头像 李华
网站建设 2026/4/18 5:17:22

<span class=“js_title_inner“>从数据供给到价值变现的闭环构建|大模型与数据要素论坛圆满落幕!</span>

汇聚来自产学研各界的顶级专家与企业领袖&#xff0c;共同探讨如何通过数据采集、标注、生产、评估、交易流通等全链路环节&#xff0c;构建“行业数据模型”的AI产品闭环&#xff0c;推动新质生产力蓬勃发展。 在大模型时代&#xff0c;数据已成为模型发展重要要素。近日&…

作者头像 李华
网站建设 2026/4/18 6:38:17

百考通「降重+降AI」双模优化功能:一键化解查重与AI检测双重压力

面对日益严格的学术审核机制&#xff0c;当代大学生不仅要应对传统查重系统的文字重复率要求&#xff0c;还需警惕各类AI内容检测工具对“生成痕迹”的识别。许多学生即便独立完成论文&#xff0c;也可能因语言风格过于规范、逻辑结构高度清晰而被误判为AI代写&#xff1b;而借…

作者头像 李华
网站建设 2026/4/18 6:39:40

2026年了,你还在用传统滚动监听做懒加载?试试这种现代方案

前言 当我们进入某个包含大量图片的网站时&#xff0c;网页内的图片却迟迟加载不出来&#xff0c;给我们带来了极差的用户体验&#xff0c;而懒加载技术&#xff0c;正是解决这类问题的有效手段之一。 传统的滚动监听懒加载方式在过去发挥了重要作用&#xff0c;但随着技术的不…

作者头像 李华