“对已全部确认提交的历史发现环,只能报告已有违约,不能任选一笔已承诺事务强行改成中止。在线控制必须在最后相关提交尚可拒绝时介入;乐观验证让全部已接受事务沿提交顺序解释,SSI则保留更宽的候选顺…”
形式陈述
执行时先保留结果,提交时再决定
乐观并发控制允许事务先读和计算,在提交前验证这段工作是否仍可接受。本页选择一种完整但保守的版本:MVCC提供固定开始快照,所有提交共用一个验证并发布的原子入口。目标是让已接受事务按提交顺序构成串行见证,不是声称所有OCC都只能采用此实现。[1, §§3.1、4]
单节点有有限键域,每个键保存已提交版本链,包括缺失版本
记
- 检查每个
是否满足 - 只要一项不满足,中止T,丢弃私有写和尚未发布的返回值,并报告键、旧来源及新写者
- 全部满足时,分配提交序号c,把
全部新版本一起发布,令相应 ,最后确认提交
所有比较与发布必须处于同一个串行化入口中。另一个事务不能在“检查通过”与“写入生效”之间发布数据。失败事务没有公共版本,成功事务的所有键同时进入下一份可见状态。
这里假定键和值的访问、内存生命周期与提交原子性由基础设施保证,不展示latch、持久日志或分布式提交。快照版本不能在仍被事务使用时回收。事务提交前的计算只修改私有状态,外部邮件、支付或不可撤销输出不能藏在可重试的计算阶段。
直觉
读集是一份尚待兑现的依据
事务可以基于旧快照工作;验证时,它逐项问“我用过的外部依据,自开始之后有没有人发布过新版本?”若没有,它的每个读在此刻仍会看到同一个来源,刚才算出的结果就能直接放在已提交事务之后。
只看数值相等不够。例如x先从0变1再变0,旧读与当前数值相同,但版本历史已改变。用最后修改序号保留这份区别,也避免把任意数值相等当作应用语义可交换的证明。本协议可能因此多中止,但不会因值恰好回来而漏过已发生的读写依赖。
按提交顺序归纳正确性
假设前m笔已提交事务已有相同的串行解释,当前数据库就是这m笔按提交顺序运行后的结果。第m+1笔T验证成功,说明R中每个键从开始到现在都没有新版本,所以当前公共状态在这些键上与T的快照相同,包括值为缺失的键。
现在把T放到这个串行前缀末尾重新运行。它每次外部读得到原来源,读取自身写也由相同程序次序给出,因此所有条件分支、私有写和返回值不变。盲写不依赖被覆盖的旧值,可以直接成为新的最终版本。原子发布之后,归纳不变量扩展到m+1。
证明同时覆盖只读事务:返回值也要等验证成功才交给调用者。若某应用允许长只读查询在更早的串行位置解释,本规则会显得保守;那需要更宽的验证目标,不能在同一实现中悄悄免掉检查。
例子与边界
两个旧决定,只接受能接在当前状态后的一个
初态x=y=1。A与B都读x、y,若另一个仍为1,就分别准备x=0与y=0。设A先进入提交入口,尚无更新,验证成功并发布x。B随后检查x,发现最后修改序号大于自己的开始序号,必须中止,不能发布y=0。
B重试时建立新快照,看到x=0,于是不再关闭y。它需要重新读、重新判断;只把旧私有写y=0再次提交,会把已经失败的计算当成新事务,失去归纳证明。这个例子对照SI时,两写集不同,SI的写写检查本来会接受两者,正是两种验证目标的差别。
拆开的验证证书会过期
若实现让A验证通过后暂不发布,又让B对同一旧公共状态验证通过,然后A、B各自写入,最终x=y=0。两次检查各自执行时都返回真,合在一起仍不安全。验证成功不是可长期持有的许可证;不把它与发布原子连接,后续写会使证书失效。
并行验证可以有正确实现,例如增加版本重检、写集合保留或其他同步,但这些步骤必须重新证明。不能删除本页原子入口后继续引用原证明。
并发盲写为何可以都提交
若C、D不读x,只分别盲写x=7和x=9,二者R均为空。按C、D提交顺序,两者都可以接受,最终x=9,串行见证就是C、D。若改成经典SI,两个重叠事务写同一键,first-committer-wins会拒绝一个。
所以本页采用快照读取,不意味着它也采用SI的全部提交规则。OCC直接选择提交顺序并验证读依据;SI保留另一组历史限制。若写值来自读x后加一,x便必须进入R,再也不是这个盲写分支。
空查询也是外部读依据
固定键域中,查询[10,13)是否为空,应把10、11、12的状态都加入R,即使结果没有返回任何记录。并发事务插入11后,验证会看见11的新版本并拒绝旧的“为空”判断。若只把返回行加入R,两笔分别插11、12的事务都会拥有空读集,非法双插入就重现了。
单节点有限域只是可复算模型。实际范围验证可以维护范围版本、索引修改证据或谓词读集合,但必须保证插入、删除及旧新键位置都落在验证覆盖中。不能只增加验证频率而继续漏掉对象。
推论与应用
最后修改序号与历史写集等价
朴素串行验证可检查所有自T开始后已提交事务U,要求
因此在固定键域、已维护最后修改序号的前提下,一次核心验证并发布需
附件还保留完整事务trace、版本和事后图检查,这些审计开销不能算作常数提交成本。若最后修改计数器溢出或允许回退,时间比较就不再可靠;若旧版本过早回收,读阶段本身已经不符合快照合同。
乐观不等于无等待或无饥饿
长读事务可能反复被新的短写事务使验证失败。无冲突时容易成功,并不保证每笔事务都在有限次重试内结束。退避、退化为锁式执行或资源保留可以改善进展,但本页不从串行安全性推导公平性。
快照验证终结任务保留每次拒绝的键与两个版本身份,并把写偏差、空区间、并发盲写和错误双验证放在同一入口中复算。与只报告“提交失败”相比,这份证书能解释失败究竟保护了哪一个外部读。
参考资料
[1] H. T. Kung、John T. Robinson,On Optimistic Methods for Concurrency Control,ACM TODS 6(2),1981,pp.213–226,§3.1三种验证条件,§4 pp.220–222串行验证。本文采用其串行验证发布思想,加上明确的固定快照与逐键最后修改序号;不是原文并行验证分支的完整实现。