悲观事务模型下的快照读流程是怎么样的?

嗯, 你说得对,读写冲突 指的应该是快照读。我又胡乱看了好一些文章,大概有一个猜想,不一定对:joy:

以下均为悲观事务:

  1. DML阶段加的锁不会阻塞快照读,因为此时还没有commit_ts,没必要阻塞,原因暂时没想清楚。这个锁只是一个占位符,和Prewrite阶段的锁不一样,出处:

    TiDB 的悲观锁实现的原理确实如此,在一个事务执行 DML (UPDATE/DELETE) 的过程中,TiDB 不仅会将需要修改的行在本地缓存,同时还会对这些行直接上悲观锁,这里的悲观锁的格式和乐观事务中的锁几乎一致,但是锁的内容是空的,只是一个占位符,待到 Commit 的时候,直接将这些悲观锁改写成标准的 Percolator 模型的锁,后续流程和原来保持一致即可,唯一的改动是:

    对于读请求,遇到这类悲观锁的时候,不用像乐观事务那样等待解锁,可以直接返回最新的数据即可(至于为什么,读者可以仔细想想)。

    TiDB 新特性漫谈:悲观事务

  2. Prewrite 阶段,
    2.1. 写事务会根据现有的 max_ts,计算 min_commit_ts
    2.2. 写事务会加个自己的 start_ts 的锁
    2.3. 快照读分两种情况
    2.3.1. 快照读在 Prewrite 该 Key 前就读取了该 Key,会把该 Key 的 max_ts 推高至快照读的 start_ts,如果下一次继续读这个 Key,判断 min_commit_ts 是否大于快照读的 start_ts,若是,则锁 Bypass
    2.3.2. 快照读在 Prewrite 该 Key 前没有读取该 Key,且 start_ts 大于 min_commit_ts,会被锁住,这应该就是 读写冲突 里的 Backoff 重试

考虑以下三个事务:

T1 T2 T3 Lock
Begin(Start TS = 1)
Prewrite(x) Begin(Start TS = 5) Lock(x, Start TS = 1)
Read(y) => Max TS(y) = 5
Prewrite(y) => Min Commit TS(y) = 6 Lock(y, Start TS = 1)
Read(y) => Ok Begin(Start TS = 7) Min Commit TS(y) > Start TS(T2) => 锁Bypass
Read(y) => Block Min Commit TS(y) < Start TS(T3) => 锁阻塞
COMMIT

参考:Async Commit 原理介绍丨 TiDB 5.0 新特性