事务 — 表里没写的那些列才是实务的大头
一句话总结
教材隔离表之外最重要的并发异常是写偏斜。
核心概念
ACID 分别是原子性、一致性、隔离性和持久性;持久性通常由预写日志(WAL)保证。
| 隔离级别 | dirty read | non-repeatable read | phantom read | write skew |
|---|---|---|---|---|
| Read Uncommitted | 标准允许 | 允许 | 允许 | 允许 |
| Read Committed | 不允许 | 允许 | 允许 | 允许 |
| Repeatable Read | 不允许 | 不允许 | 标准允许 | 允许 |
| Serializable | 不允许 | 不允许 | 不允许 | 不允许 |
除 Serializable 外都允许写偏斜。PostgreSQL 请求 Read Uncommitted 时实际按 Read Committed 运行;其 Repeatable Read 使用快照隔离,写冲突会报错,因此提高隔离级别必须配套重试。
经典例子是两名值班医生都读取 count(*) = 2,然后更新不同的行,最终无人值班。目标行明确时可用 SELECT ... FOR UPDATE;冲突少时可用乐观锁;跨行不变量可用 Serializable。若能用唯一索引或排斥约束表达,约束最可靠。
重试规则
重试块只放数据库操作,外部支付或邮件必须移到提交之后。限制重试次数并退避。按 SQLSTATE 分类:序列化失败是 40001,死锁是 40P01;约束违反不应重试。
下个练习
在 PostgreSQL 的两个会话中复现更新丢失与写偏斜,再使用锁和 Serializable 修复。