知识卡片

可串行化快照隔离靠检测过时前提实现乐观并发控制

结构图卡

内容

两阶段锁定是悲观并发控制——出于”宁可信其有”的态度提前用锁挡住一切潜在冲突;可 串行化快照隔离(SSI)走的是乐观并发控制路线——先假设一切安好、让事务照常并发执行, 只在事务想提交的那一刻才检查隔离是否真的被违反,若违反才中止重来。SSI建立在 [[快照隔离靠MVCC事务ID可见性规则实现读不阻塞写]]之上,核心洞察是:写偏差之所以 发生,是因为事务基于一个”前提”(比如”当前有两名医生在班”)采取了写入动作,而这个 前提在事务提交那一刻可能已经过时。数据库要检测这种”基于过时前提”的写入,需要盯住 两种情形。第一种是检测对旧MVCC版本的读取:如果事务A读取快照时因为MVCC可见性规则 忽略了另一个尚未提交的事务B的写入,而A提交时B已经提交了,说明A当初读到的前提已经 过期,A必须中止——但这个检测要延后到A真正想提交时才做判断(而不是读到旧值那一刻 就中止),因为如果A后续只读不写、或B最终也中止了、又或者A提交时B依然没提交,那么 中止A就是不必要的,SSI要尽量避免这类无谓中止以保留快照隔离长时间读取的优势。第二种 是检测影响之前读取的写入:借用两阶段锁定里索引范围锁的思路,在索引上记录”哪些事务 读取过这个范围”,但这里的记录不阻塞其他事务,只是像警示标记一样通知:”你读过的 东西可能已经不是最新的了”,一旦有事务基于此写入并率先提交,另一个读过同一范围又 还没提交的事务就必须中止。相比2PL,SSI最大的优势是事务完全不需要阻塞等待别人持有的 锁——读不阻塞写、写不阻塞读——查询延迟更可预测;相比串行执行,SSI不受限于单核吞吐量, 可以像FoundationDB那样把冲突检测分布到多台机器上。

结构图

flowchart TD
    A[SSI: 基于快照隔离的乐观并发控制] --> B[事务照常并发执行, 提交时才检查]
    B --> C[情形一: 读取时忽略了尚未提交的写入]
    C --> C1[提交时若该写入已提交, 说明前提过期]
    C1 --> C2[中止本事务]
    B --> D[情形二: 写入影响了他人之前的读取]
    D --> D1[索引上记录谁读过这个范围, 不阻塞只标记]
    D1 --> D2[有读者事务尚未提交则该事务中止]
    A -.相比2PL不阻塞等锁, 相比串行执行不受单核限制.-> A

参考来源

- 位置:《数据密集型应用系统设计》第七章《事务》"悲观与乐观的并发控制""基于过时 前提的决策""检测旧MVCC读取""检测影响之前读取的写入"(源文件: _epub-src/ch7_split_006.html) - 结论依据:原文说明SSI是基于快照隔离的乐观并发控制技术,提交时才检查隔离是否 被违反,详述检测对旧MVCC版本读取和检测影响先前读取的写入两种情形,并说明SSI 相比2PL不需要阻塞等锁、相比串行执行不受单核吞吐量限制,直接支撑本卡片的结构 梳理。 - 原始内容:可串行化快照隔离是一种乐观的并发控制技术……当一个事务想要提交时, 数据库检查是否有什么不好的事情发生……为了防止这种异常,数据库需要跟踪一个 事务由于MVCC可见性规则而忽略另一个事务的写入……这个过程类似于在受影响的键 范围上获取写锁,但锁并不会阻塞事务。