TIKV 源码学习笔记 -- 分布式事务接口 CheckTxnStatus/ ResolveLock
作者: ylldty 原文来源:https://tidb.net/blog/3dbc61c7
前言
本篇文章将会对锁冲突场景下,常用的解决锁冲突的接口,进行个人对代码的理解与解析,希望对大家理解 TIKV
分布式事务有所帮助
CheckTxnStatus
这个接口的作用比较明显,主要是事务中,遇到锁冲突 (一般是悲观事务过程中加悲观锁,或者 prewrite
过程中加的锁),从锁信息中获取到其 Primary KEY
,进而通过这个接口来看 primary
key
的当前事务状态。事务状态可能是已经提交,已经回滚,或者还在事务中。
特别的,如果发现 Primary KEY
的锁已经过期了,CheckTxnStatus
还会主动将其进行保护模式的回滚。
参数
假如目前有两个并发事务,t1
和 t2
t1
在事务过程中,发现了 t2
的锁,因此 t1
调用了 CheckTxnStatus
接口来查看 t2
当前的状态
primary_key
: 需要查看的t2
主键KEY
lock_ts
:t2
的start_ts
caller_start_ts
:t1
的start_ts
current_ts
: 当前的ts
rollback_if_not_exist
: 如果没有发现任何提交记录或者回滚记录的时候,是直接回滚还是返回错误force_sync_commit
:async_commit
的场景下,是否强制推进async_commit
进程,否则返回uncommitted
resolving_pessimistic_lock
:false
代表本意是想解析prewrite lock
,true
代表本意是想解析悲观锁。verify_is_primary
: 验证主键上面的锁是主键锁 ( issue 42937 ),目前默认开启该校验功能
简化代码
和其他接口一样,首先需要获取
Primary
的锁信息如果找到的锁是 符合预期 的
t2
的锁,那么调用check_txn_status_lock_exists
首先需要校验这个
lock
的合法性:如果verify_is_primary
参数是true
,结果发现这个锁信息中的primary key
和请求参数的primary
对应不上,那么需要返回错误PrimaryMismatch
,这种情况可能是primary key
被替换了。Corner Case
:但是存在一个特殊情况 (lock
是悲观锁 &&resolving_pessimistic_lock
是false
)。就是说本来想要解析的是prewrite lock
,结果发现是悲观锁,而且锁的primary
主键key
还对应不上,这种场景下会网开一面并不会报错,而是会清理悲观锁,并且使用check_txn_status_missing_lock
来进行进一步查看事务状态。这种场景的出现可能是因为悲观事务的acquire_pessimistic_lock
接口被stale
调用导致的如果是
use_async_commit
类型的lock
,非强制模式下 (force_sync_commit
为false
),直接返回Ok(TxnStatus::uncommitted)
后续可能会进行重试。否则的话,继续执行如果
lock
是prewrite lock
的话,是符合预期的进一步判断lock
的过期时间如果已经过期,那么直接回滚,返回
TxnStatus::TtlExpire
。(这里的回滚好像是非保护模式的?按理说应该对primary lock
进行保护模式的回滚,笔者比较疑惑)如果还未过期,那么更新
lock
的min_commit_ts
,并且返回TxnStatus::uncommitted
如果
lock
是悲观锁的话,需要使用check_txn_status_from_pessimistic_primary_lock
进一步处理特别地,如果悲观锁信息显示该
lock
是通过公平锁功能写入的,那么这个lock
需要进一步进行检查验证,防止 issue 43540 ,进一步查看该lock
对应的事务是已经提交或者回滚,防止其是stale
的lock
。如果确实没有任何提交记录或者回滚记录,那么可以才可以认为该悲观锁是可用的有效的。否则的话,直接清楚该悲观锁,返回事务状态即可。如果悲观锁已经过期
如果预期检查的
lock
就是悲观锁,那么只需要清理悲观锁,返回Ok(TxnStatus::PessimisticRollBack)
即可,无需回滚如果预期检查的
lock
是prewrite lock
,我们就需要清理悲观锁的同时,还需要留下回滚的记录 (非保护模式下,笔者目前不太了解为何还是非保护模式)如果悲观锁未过期,那么更新
lock
的min_commit_ts
,并且返回TxnStatus::uncommitted
如果没有找到锁,或者找到的锁是不是符合预期的
t2
的锁,属于非预期场景,那么调用check_txn_status_missing_lock
check_txn_status_missing_lock
这个函数我们应该很熟悉了,这个函数在 rollback
、cleanup
函数中也会被调用,但是由于 MissingLockAction
的不同,逻辑稍微有些变化:
如果发现有本事务的
OverlappedRollback
的记录或者回滚记录 (SingleRecord::Rollback
),说明已经回滚完成,直接返回OK
如果发现有本事务提交记录的话,返回
ErrorInner::Committed
如果没有找到任何本事务
write
记录的话,属于非预期场景如果
rollback_if_not_exist
为false
,那么直接返回ErrorInner::TxnNotFound
如果
resolving_pessimistic_lock
参数为true
的话,就是说目标是解析悲观锁,结果并没有发现该锁,这时候会返回Ok(TxnStatus::LockNotExistDoNothing)
如果
rollback_if_not_exist
为true
,那么需要进行保护模式的回滚操作:调用
mark_rollback_on_mismatching_lock
在这个LOCK
上面添加回滚LockTS
标记,这样这个lock
所涉及的事务在提交后,如果发现自己的commitTS
和LockTS
重叠的话,需要设置一下overlap
标记调用
make_rollback
写入保护模式的rollback
记录,确保这个回滚记录不会被删除删除
collapse
以前的非保护rollback
记录
CheckSecondaryLocks
CheckSecondaryLocks
接口主要是应用与 Async Commit
所用,用来查看异步 commit
的过程中,通过 primary lock
上面的 secondary
来查看所有的 prewrite lock
,进而分析事务到底是否提交。
/// Check secondary locks of an async commit transaction.
///
/// If all prewritten locks exist, the lock information is returned.
/// Otherwise, it returns the commit timestamp of the transaction.
///
/// If the lock does not exist or is a pessimistic lock, to prevent the
/// status being changed, a rollback may be written.
参数
keys:事务涉及到被加锁的 keys
start_ts: 事务的开始 ts
简化代码
对每个
key
查询所对应的lock
如果通过某一个
key
发现了提交或者回滚记录,那么直接可以break
,返回结果。如果没有记录,也没有找到锁的话,那么就需要回滚,并且是以保护模式下进行回滚,然后
break
,返回结果。否则的话需要遍历所有的
key
,收集lock
信息如果如预期一样查询到了事务的
lock
,那么就会使用check_status_from_lock
进行进一步检查如果
lock
是悲观锁和
checkTxnStatus
一样,如果lock
是公平锁冲突加锁的话,需要进一步查看提交、回滚、无记录状态。如果是提交或者回滚状态,那么直接可以终止CheckSecondaryLocks
返回结果。如果是无记录状态的话,可以将其当做普通的悲观锁悲观锁是非预期状态,这个时候需要清理悲观锁,将其当做无记录也没有找到
lock
的场景来看,也就是执行回滚操作,然后终止CheckSecondaryLocks
如果
lock
是prewrite lock
,符合预期,返回锁信息,继续检查其他key
的状态如果没有
lock
,或者没有查询到预期事务的lock
,那么就会check_determined_txn_status
进一步查询提交或者回滚的记录
ResolveLock
通过 checkTxnStatus
查询到 primary key
的事务状态后,就需要 ResolveLock
对 secondary key
进行提交或者回滚。如果 primary key
已经提交了,那么 ResolveLock
对 secondary key
进行提交。如果 primary key
已经回滚了,那么 ResolveLock
对 secondary key
进行回滚。
参数
start_ts:事务的开始 ts
commit_ts: 事务的提交 ts。当需要回滚的时候,该值为 0;否则的话,该值不为 0
resolve_keys: 需要提交或者回滚的
secondary keys
简化代码
代码非常简单了,直接调用提交或者回滚的函数即可。注意对于 secondary key
来说,回滚是非保护模式的。
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/8aa965e88640494ca4def6332】。文章转载请联系作者。
评论