TiDB悲观事务细节的深入思考

转载请附本文链接:https://blog.csdn.net/maxlovezyy/article/details/103576337

本文讨论一下关于TiDB在知乎发布的悲观事务设计的一些实现细节的思考,记录下来免得忘了,下面以QA的形式描述。

  • Q1: 中提到“对于读请求,遇到这类悲观锁的时候,不用像乐观事务那样等待解锁,可以直接返回最新的数据即可(至于为什么,读者可以仔细想想)”,怎么理解?
    A1:文中提到的这类悲观锁其实指的是占位锁,这样就很明确为什么遇到占位锁可以直接返回数据而不需要等了,因为这时候和Percolator的2PC没关系,还没有开始Percolator的prewrite呢,所以直接返回是没问题的,后续如果有prewrite,也一定是client先上完占位锁之后,再走Percolator流程,会先获取start_ts,而这个start_ts是必定大于刚read事务的start_ts的,所以不会出现幻读和不可重复读。
    具体实现上,TiKV的Get接口在读取的时候如果遇到了锁,会先判断锁的类型,如果是Pessimistic的悲观锁而非Put(Prewrite数据的锁类型)锁,则直接返回数据。这里需要说明的是目前TiKV的实现中这个Pessimistic锁是持久化了的。

  • Q2: 一个事务涉及多个key怎么理解?在Q1中,主要讨论的是一个key怎么做的。那么多个key呢?比如一个读事务txn-r通过一个谓词条件过滤读,另一个写事务txn-w写N条数据,影响txn-r谓词的多条数据,这时候怎么保证txn-r的快照一致性?
    A2: 可以看出,这种情况下txn-r很可能遇到一批占位锁,直接返回最新版本数据;一批prewrite锁,需要等待决议。这样的话有没有可能事务的start_ts小于ts,然后出现lazy的commit,也就是commit_ts小于ts,但是还游荡着没有commit,过一会再commit,导致小于ts最新的数据会发生变动,进而txn-r读到了不一致的数据?其实在SI的情况下是不会出现这种问题的,下面分析一下原因。
    对于上面说的情况,可以看出txn-w的commit_ts小于txn-r的start_ts,正是由于这个情况,可以看出txn-r开始的时候,txn-w的所有修改(prewrite)都一定会被txn-r看见,因为txn-w既然获取commit_ts,那么这时候一定已经都prewrite完了。另一种情况是txn-w的commit_ts大于txn-r的start_ts,这种情况就更没有问题了,整个txn-r是见不到txn-w的修改的。因此Q2的问题不是问题。

  • Q3: 有没有可能出现ABA问题,即你想说的数据版本和预期的不一致?比如client发起了一个lock,但过程中数据被其他事务更新过了,如果继续lock则lock了非预期的数据,强行上锁就会出现不一致,这该怎么办?
    A3: 目前TiDB的做法是每个pessimistic锁请求都带有一个事务级别的时间戳作为版本号,如果数据当前版本号大于锁请求带来的版本号则说明数据已经有变更过了,返回失败,client需要新拿一个版本号,重做相关的逻辑。可以理解这个过程类似CAS。

你可能感兴趣的:(分布式)