事务与隔离级别
PostgreSQL 支持标准的四种隔离级别,但两个关键点与 MySQL 不同:默认是读已提交(RC)而不是可重复读;可重复读下 PG 不防幻读(没有间隙锁),要严格防幻读只能上可串行化。
三种并发问题
| 问题 | 现象 |
|---|---|
| 脏读 | 读到其他事务未提交的数据 |
| 不可重复读 | 同一事务内两次读同一行结果不同 |
| 幻读 | 同一事务内两次范围查询行数不同 |
四种隔离级别
| 级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交(RC,默认) | 解决 | 可能 | 可能 |
| 可重复读(RR) | 解决 | 解决 | 可能 |
| 可串行化 | 解决 | 解决 | 解决 |
快照语义:PG 的实现方式
PG 的隔离级别差异就是快照的生成时机:
- RC:每条语句开始生成新快照。事务内两条 SELECT 之间其他事务提交了,第二条能看到新数据
- RR:事务第一次读时生成快照,整个事务复用。同一事务内读到的数据始终一致
- 可串行化:RR 的快照加冲突检测
PG 的 RR 不防幻读:因为没有间隙锁,另一个事务在范围内插入新行后,当前事务虽然看不到(快照),但这是靠快照而非锁实现的“伪防幻读”。需要严格防幻读(如金融对账)时,用可串行化级别。
SSI:可串行化快照隔离
PG 的可串行化不是锁串行化,而是乐观冲突检测:事务并发执行,提交时检测读写依赖是否形成环(SIREAD 锁跟踪读集),检测到冲突就回滚其中一个(报错 40001)。
适用场景:真正需要可串行化保证的并发写入(如并发修改同一范围数据),但冲突率高时回滚多,性能差。多数业务 RC 足够。
与 MySQL 的对比
| 维度 | PostgreSQL | MySQL |
|---|---|---|
| 默认级别 | 读已提交 | 可重复读 |
| RR 防幻读 | 不防(无间隙锁) | 防(临键锁) |
| 可串行化实现 | SSI 乐观冲突检测 | 全表锁串行化 |
| 隔离机制 | 纯快照 + 版本链 | MVCC 快照读 + 锁当前读 |
面试高频结论:MySQL 的 RR 靠临键锁防幻读,代价是间隙锁冲突和死锁;PG 的 RC 默认更简单,需要强一致时用 SSI,机制不同各有取舍。
面试追问
- PG 默认隔离级别? 读已提交,和 MySQL 的默认可重复读不同
- PG 的 RR 防幻读吗? 不防。没有间隙锁,幻读只能靠可串行化级别解决
- SSI 是什么? 可串行化快照隔离:乐观并发控制,提交时检测读写冲突,冲突回滚(40001)
- RC 和 RR 的快照区别? RC 每条语句新快照,RR 事务复用第一个快照
- 什么场景用可串行化? 需要严格防幻读的并发写入(金融对账)。冲突率高时回滚多,性能差,多数业务 RC 足够