隔离性:实现悲观协议,除了锁还有别的办法吗

164 阅读2分钟
image.png

两阶段封锁(Two-Phase Locking,2PL)

这里的两个阶段指加锁阶段和释放锁阶段。2PL 的关键点就是释放锁之后不能再加锁。

保守两阶段封锁协议(Conservative 2PL,C2PL),事务在开始时设置它需要的所有锁。

严格两阶段封锁协议(Strict 2PL,S2PL),事务一直持有已经获得的所有写锁,直到事务终止。

强两阶段封锁协议(Strong Strict 2PL,SS2PL),事务一直持有已经获得的所有锁,包括写锁和读锁,直到事务终止。

串行化图检测(SGT)

串行化图的构建规则是这样的,事务作为节点,当一个操作与另一个操作冲突时,在两个事务节点之间就可以画上一条有向边。

具体来说,事务之间的边又分为三类情况:

  1. 写读依赖(WR-Dependencies),第二个操作读取了第一个操作写入的值。
  2. 写写依赖(WW-Dependencies),第二个操作覆盖了第一个操作写入的值。
  3. 读写反依赖(RW-Antidependencies),第二个操作覆盖了第一个操作读取的值,可能导致读取值过期。

根据上述情况画图,如果产生了一个有向无环图(Directed Acyclic Graph,DAG),就说明相关事务是可串行化执行的,不需要中断任何事务。

工程实现:CockroachDB

CockroachDB 做了一个关键设计,读时间戳缓存(Read Timestamp Cache),简称 RTC。

当执行任何的读取操作时,操作的时间戳都会被记录在所访问节点的本地 RTC 中。当任何写操作访问这个节点时,都会以将要访问的 Key 为输入,向 RTC 查询最大的读时间戳(MRT),如果 MRT 大于这个写入操作的时间戳,那继续写入就会形成 RW 依赖。这时就必须终止并重启写入事务,让写入事务拿到一个更大的时间戳重新尝试。

RTC 是一个大小有限的,采用 LRU(Least Recently Used,最近最少使用)淘汰算法的缓存。为了应对缓存超限的情况,会将 RTC 中出现过的所有 Key 上最早的那个读时间戳记录下来,作为低水位线(Low Water Mark)。如果一个写操作将要写的 Key 不在 RTC 中,则会返回这个低水位线。


此文章为6月Day15学习笔记,内容来源于极客时间《分布式数据库30讲》