tidb 的悲观事务&&可重复读 RR 级别中,快照读 select 不需要检测 lock 吗?

TIDB 的乐观事务默认是 SI 级别,对于 Select 语句,如果读取的 rowid 有 lock 记录,那么需要等锁、清锁、重试

TIDB 的悲观事务在 RC 提交读隔离级别中,对于 Select 语句是快照读,即使 rowid 有 lock 记录,也不需要等锁,直接读取 write 记录最新提交的 value 即可

但是悲观事务默认隔离的 RR 可重复读级别,对于 Select 语句快照读,是和快照读一样不需要检测 lock 记录呢,还是和 si 一样,需要 tikv 提前检测lock记录,从而需要等锁呢?

快照读一样不需要检测 lock 记录

快照读只要是确认是提交过的就行,不需要考虑锁的问题

TiDB 实现了快照隔离 (Snapshot Isolation, SI) 级别的一致性。为与 MySQL 保持一致,又称其为“可重复读” (REPEATABLE READ)。
与 MySQL 可重复读隔离级别的区别
MySQL 可重复读隔离级别在更新时并不检验当前版本是否可见,也就是说,即使该行在事务启动后被更新过,同样可以继续更新。这种情况在 TiDB 使用乐观事务时会导致事务回滚,导致事务最终失败,而 TiDB 默认的悲观事务和 MySQL 是可以更新成功的。

1 个赞

这里的锁是不一样的,乐观锁和悲观锁中的读写冲突指的是,两阶段提交的时候,Prewrite 加的锁会阻塞能看到该锁的事务的读,原因是写下该 lock 的事务可能已经获取了 commit ts,且 commit ts 小于看到锁的事务的 start ts,这时候 select 都要等锁。
你描述的悲观锁不用等锁,指的是悲观锁两阶段提交前,会加一把锁,确保两阶段提交的时候不会发生 write conflict 的错误,这里的事务一定还没获取 commit ts,所以悲观锁不会阻塞读(和乐观锁一样,悲观事务提交仍要走一遍完整的 Percolator 提交流程,只有当所有悲观锁被 Prewrite 成 Percolator 的锁才会到 Commit 阶段)。

1 个赞

通过 DEBUG tikv 的代码:
image

可以总结有两种场景:

  • 如果只有悲观事务,那么 RC/RR 级别的 select 快照读会忽略其他悲观事务加的锁。例如有两个悲观事务,一个事务t1 执行 select for update,不提交,另一个事务 t2 执行 select 快照读。虽然 t2 快照读发送给 tikv 的请求仍然是 si 级别的,也检测到了 t1 事务所加的悲观锁,但是会主动忽略这个锁,而直接返回上一个 commit 记录,无需等锁或者重试

  • 如果悲观事务和乐观事务共存,RC/RR 级别的 select 快照读可能会遭遇等锁、重试。例如两个事务,一个事务是乐观事务 t1,当前正在进行二阶段提交,目前刚刚 prewrite 完成,还未能 commit。事务 t2 是悲观事务,执行 rc 级别的快照读,此时快照读拿到时间戳 read_ts2。这个时候 t2 的快照读会检测到 t1 的 lock 乐观锁。如果无法确定这个乐观锁最后提交的时间 commit_ts1 到底会大于 read_ts2 还是小于 read_ts2,因此需要等锁、重试。防止事务 t2 快照读成功后,突然又有乐观事务把事务提交到了 read_ts2 之前,正确性得到了提升。

第二种情况同样适用于悲观事务进行二阶段提交时,prewrite 完成后,因为此时悲观事务的悲观锁被修改为乐观锁。

1 个赞

@lilinghai @tidb菜鸟一只 @dba远航 @lemonade010
多谢大家的回复

快照出现的目的就是为了消除部分锁,使得读写可以同时进行

1 个赞

此话题已在最后回复的 60 天后被自动关闭。不再允许新回复。