Skip to content

算法Algorithm

可串行化快照隔离

Serializable snapshot isolation · SSI

在SI前提下证明环必含两条相邻并发读写反依赖,以可复算危险结构保守拒绝提交,并限定只读快照、历史保留和重试条件。

形式陈述 ​

在SI上增加拒绝证书 ​

快照隔离让事务读开始时的已提交视图,并以first-committer-wins排除重叠事务对同键的双提交。可串行化快照隔离(SSI)保留这些基础规则,再记录读写反依赖,拒绝可能形成串行矛盾的事务。读者不会因这个逻辑读标记而把写者挡在共享锁之外;但提交仍可能失败。

本页继续用多版本依赖图的版本、wr/ww/rw与已提交投影。各开始事件s、提交事件c严格排序;T与U并发,指二者生命区间重叠,即 s(T)<c(U) 且 s(U)<c(T)。一条rw边只有两端并发时,才计入下面的危险结构。

在满足经典SI前提的已提交历史中,每个有向环都包含

Tin→rwTpivot→rwTout,

两条边各自连接并发事务,并且可以选 Tout为该环最早提交者。相邻顶点不同,但 Tin与 Tout允许是同一事务,二事务写偏差正是这种情况。[1, §2.2 Theorem 2.1;2, §3.2]

这样的双边链称为危险结构;它是环的必要结构,本身不是环的充分条件。一个控制器可以保守地中止链上一笔尚未提交事务,即使实际上不存在返回路径。已经确认提交的事务不能事后撤销来补证明。

本页选用可完整复算的提交参考器 ​

为把在线决定做成有限程序,本页保留全部历史,不实现生产系统的压缩读标记或垃圾回收。候选事务T提交时,在同一个原子入口执行:

  1. 先作SI写写检查;若自s(T)以来已有提交改过T要写的键,直接中止
  2. 暂把T视为此刻提交,连同既有全部已提交事务重建真实版本图;未完成、已中止的其他事务不加入
  3. 寻找上述双边链,并要求 c(Tout)≤c(Tin)、c(Tout)<c(Tpivot);前一个等号只可能是两端同一事务
  4. 若链的 Tin只读,还要求 c(Tout)<s(Tin)。满足这些条件的链作为拒绝证书;存在则中止当前候选T,否则原子发布其私有写并确认提交

这是带提交次序和只读条件的保守SSI参考验证器,不是论文中低开销读写钩子的逐行移植。它选择当前提交者作为受害者,是因为此前已接受的完整历史没有符合条件的危险结构;新出现的证书必涉及当前候选。重新构图时保留过去读过什么,才能发现读者早已提交、写者最后到达的边。

直觉

为什么环里必有相邻两条反依赖 ​

先从SI得到两个时序事实。wr边A→B要求A版本进入B快照,所以 c(A)<s(B)。ww边A→B也要求这个先后,否则两写者重叠且同写一个键,会被SI拒绝。对任意依赖边A→B,总有 s(A)<c(B);rw情况下,若B在A开始前已经提交,A就不会仍读更旧版本。

现在选环中最早提交的事务O,前驱为P,再前驱为I。若P与O不并发,P先结束会违背O最早提交,O先结束又会违背P→O要求的 s(P)<c(O)。因此P、O并发,边只能是rw。

同理,P开始在O提交之前。若I与P不并发,I先结束便早于P开始、从而早于O提交;P先结束又违背I→P的开始/提交约束。于是I、P也并发,另一条边同样是rw。两端I=O时论证仍成立。这就解释了为什么只跟踪并发rw双边链,也能拦住所有环。

为什么只读条件也成立 ​

在刚才选出的环中,如果I只读,其前一条入边不可能是ww或rw,因为两者的终点都会写。因此前驱J通过wr连到I,满足 c(J)<s(I)。O又是环中最早提交者,所以 c(O)≤c(J)<s(I)。

只读事务在形成危险结构时不一定有问题;但要成为这个真正环里的I,O必须在它取快照之前提交。参考器第4步利用这一必要条件,消除一部分无害链,并未把“只读”直接当成永不出错的标记。[2, §4.1 Theorem 3]

当前提交者为什么总来得及拒绝 ​

对已接受事务数归纳。空历史无危险结构。一次候选若通过,扩展历史仍无符合条件的危险结构,所以由定理也无环;若不通过,它尚未发布,删除这个候选后旧历史原样保留。后来的事务可能给已提交者增加反依赖,但在它自己的提交入口会重新检验,不能让新环越过检查。

这个证明依赖完整历史和提交原子性。只检查候选自己的incoming/outgoing是否都非空,会漏掉候选恰是链端点、另一个已提交事务才是pivot的情况。只保留仍活跃读者,则会漏掉已经结束的只读R指向后提交P的边。

例子与边界

二环中两端可以是同一个名字 ​

A、B都读x=y=1,分别准备x=0、y=0,先让A提交。此时只有A进入已提交图。B尝试提交时,两条反依赖一起可见;取I=A、pivot=B、O=A,O与I是同一事务,且A比B更早提交。参考器拒绝B,A的提交不必被撤销。

若代码错误要求I、pivot、O三个名字全不同,这个最常见的写偏差二环便会逃过检查。另一方面,若两个事务写同一键,它们应先被SI写写规则处理,不能在危险结构阶段才临时补回被省掉的SI前提。

只读三环与无环误拒绝用同一张时间表 ​

P在事件1开始,读y=0;Q在2开始,写y=1并在3提交;R在4开始,读取x=0及Q的y=1,在5提交;P最后准备在6发布x=1。P提交前旧图只有Q→wr R;加入P后出现R→rw P→rw Q,并由Q→wr R闭环。

危险结构取I=R、pivot=P、O=Q。Q的提交3早于R快照4,也早于R、P的提交5、6,两个时序过滤都不能排除它。虽然当前候选P是pivot,参考器的正确性不依赖每次都碰巧如此。

若R只读x、不读y,时间不变,两条rw依旧成立,Q→R却消失。此时R、P、Q是合法串行顺序,但同一保守参考器仍会拒绝P。这个反例把“结构检测成功”与“准确证明已有环”分开;若要减少这次误拒绝,可以做完整图环检测,却会采用不同的检测成本和状态合同。

快照刚建立时没有冲突,不代表已经安全 ​

只读I若要完全免除SSI跟踪,需要知道没有与它并发的更新事务P,可能成功提交并带有指向快照前已提交O的rw边。P在I开始时尚未完成,未来还可能读取旧版本而产生这条边;仅看当前两个布尔标记都为假,不够证明安全。[2, §§4.2–4.3]

一个容易核实的充分入口是:事务提前声明只读、后续接口禁止写,且建立快照时没有任何活跃更新事务。不能让一次“尚未写”的普通事务借此豁免后再转成写事务。以后新开始的更新者都能看见该快照之前的已提交版本,不可能再形成指向更早O的相应rw边;已结束的更新者又不与只读者并发。因此该快照不会参与SI串行异常。前提还包括更新事务本身受同一SSI规则控制,不能让不参与协议的写入绕过依赖记录。

更一般的安全快照可以等待开始时并发更新者的结局后判定;等待的是安全证明,不是随便睡固定毫秒。本文附件只核这些明确时序,不实现实际数据库的deferrable事务或有限内存回收策略。

推论与应用

读标记不是阻塞式共享锁 ​

论文实现可在读时记录非阻塞的SIREAD标记,写者遇到它时记录反依赖而不等待该读者释放;读旧版本时也要检查已有较新写者。两种发现次序都不可少。读者提交后,标记还可能被并发、较晚提交的写者需要,不能按普通共享锁的提交释放规则直接清空。[3, §3.1]

本页完整历史参考器通过重建两种时序的边规避这项内存工程,但状态随历史增长,没有恒定空间保证。设候选及已提交事务数为n,版本数V、外部读条目数R;构图粗略需 O(1+V+RV+n),朴素枚举pivot的入出边对最坏 O(n3)。两者合计 O(1+V+RV+n3),还应计读写记录、版本及边的存储。有限测试程序的吞吐不能用来代表数据库产品性能。

重试与协议范围 ​

被拒绝的事务必须重新取得快照并重跑全部业务计算。已经确认提交的事务保持原决定;不能撤销用户可见结果来“修图”。若同一拒绝证书的其他事务已全部完成,新开始的重试与它们不再并发,不会再由那一组并发边得到同一证书;新的并发事务仍可能造成另一轮失败,故没有确定重试次数或无饥饿结论。

谓词查询必须覆盖潜在插入、删除和条件变化。固定键域附件把范围展开成包含缺失状态的逐键读;真实系统用非阻塞范围证据或等价机制。仅跟踪返回行、允许脏来源,或者不执行SI写写规则,都会使本页的危险结构定理前提失效。

快照验证终结任务对同一输入分别运行SI、OCC与SSI,报告两条rw的键、并发区间、O最早提交及只读过滤结果,再把误拒绝例交给完整图拓扑序核对。安全证明、保守性和实现成本由三份不同证据负责。

参考资料

[1] Alan Fekete等,Making Snapshot Isolation Serializable,ACM TODS,2005,§2.2 Theorem 2.1,p.506。原证明选环最早提交者,再分析两个前驱;本页沿该原理完整重述,不把静态程序图与实际事务图混用。

[2] Dan R. K. Ports、Kevin Grittner,Serializable Snapshot Isolation in PostgreSQL,PVLDB 5(12),2012,§3.2–3.3.1、§4.1–4.3,pp.1853–1855。采用提交次序与只读条件、安全快照定义;没有把该历史论文写作当前产品配置指南。

[3] Michael J. Cahill、Uwe Röhm、Alan D. Fekete,Serializable Isolation for Snapshot Databases,SIGMOD 2008,§2.2、§3.1,pp.731–734。采用并发反依赖和非阻塞读证据;附件为完整历史的提交参考器,不宣称复现其优化实现或实验结果。

关系图谱7 个相邻概念 · 1 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组