形式陈述 ​
经典 SQL-92 用 事务调度 中三类现象描述四个隔离级别。P1 脏读指事务读取另一个尚未提交、后来可能中止的写;P2 不可重复读指同一事务两次读取同一项,期间另一事务提交修改或删除,结果改变;P3 幻读指同一谓词查询重复执行,期间另一事务提交插入、删除或更新,使结果集合成员改变。
按常见现象表:READ UNCOMMITTED 允许 P1、P2、P3;READ COMMITTED 禁止 P1;REPEATABLE READ 再禁止 P2;SERIALIZABLE 还禁止 P3。这里的“禁止”必须说明采用标准文字的窄例型解释,还是禁止可能导致相应异常的更宽解释。Berenson 等人指出,原标准文字存在歧义,仅列三种现象也不能完整刻画实际锁协议和多版本系统。
数据库实现通常还无条件禁止 P0 dirty write:一个事务不能覆盖另一事务尚未提交的同一项,否则任一方 abort 时都难以恢复正确版本。P0 不在经典三行递进表的中心,却说明“低隔离”也不是任意交错。lost update、读偏斜和写偏斜同样需要超出 P1–P3 名称的精确历史定义。
快照隔离不是 ANSI 四级中的一层。经典 SI 可以避开 P1、P2、P3 的表面实例,仍因读写反依赖产生 write skew,从而不满足可串行化。产品把某个标签映射到 MVCC 快照或锁实现时,应以实际历史保证为准,而不是从名称倒推出机制。
直觉
四个名字像风险筛网:级别越高,标准表格承诺挡住的可见变化越多。但筛网只按列出的孔径分类;若异常从“跨两行约束”这样的另一形状穿过,勾满 P1–P3 仍未必得到串行历史。
隔离级别也不是性能旋钮的纯数字刻度。READ COMMITTED 在每条语句取新快照,与用短读锁实现时,事务内观察和阻塞行为可能不同。两者都可符合某个名称,却需要不同的应用推理。
Berenson 的关键批评并非要求再背一张更长表,而是指出“禁止某个结果”与“禁止可能导致该结果的操作模式”会得到不同历史集合。规范、产品手册和论文若分别采用窄、宽解释,同名级别便不能直接比较。
例子与边界
初值
T1: read x -> 10
T2: write x := 20; commit
T1: read x -> 20; commit
WHERE salary > 100,
REPEATABLE READ 若只锁住已读行,可能稳定每行值却漏掉谓词范围内的新插入;要禁止幻读,锁实现通常还需谓词锁、范围锁或等价验证。反之,多版本实现不阻塞插入,也可以通过序列化依赖检测在提交时中止危险事务。
“SERIALIZABLE”若作为规范,应保证已提交事务等价于某个串行执行;仅通过几个固定测试没有看见 P1–P3,不足以证明这一点。SQL 级别也不规定崩溃恢复、持久性或分布式提交,这些是独立维度。
应用层重试也会影响观察。可串行化实现可以为排除环而返回 serialization failure;事务重新执行后成功,并不表示第一次执行“其实也是合法的”。测试工具应把中止结果纳入调度,而不是只收集最终成功的 SQL。
推论与应用
应用选择级别时,应先列出业务不变量依赖的读写模式,再核对数据库手册给出的实际语义。需要在读后更新同一行时,显式 SELECT ... FOR UPDATE 可能比依赖名称更清楚;跨行谓词则要检查系统是否提供真正可串行化或专门约束。
测试应构造完整并发调度并观察提交/中止,而不是顺序运行两段 SQL。系统可能通过中止其中一笔事务来维护高隔离;把任何中止都算作测试失败,会误判正确的序列化控制。
部署前还应核对读写语句的例外:只读事务是否取得固定快照,SELECT FOR UPDATE 是否改变可见性,DDL、序列对象和外部函数是否服从同一隔离边界。供应商对这些对象常有单独规则,不能从会话级标签自动外推。
参考资料
- Hal Berenson et al., “A Critique of ANSI SQL Isolation Levels,” Proceedings of SIGMOD 1995, pp. 1–10。
- ISO/IEC 9075-2, Information Technology—Database Languages SQL—Part 2: Foundation, transaction isolation clauses。
- Atul Adya, Weak Consistency: A Generalized Theory and Optimistic Implementations for Distributed Transactions, MIT PhD thesis, 1999。