TiDB 锁行为分析
作者: 大鱼海棠原文来源:https://tidb.net/blog/bbcb5a11
TiDB 乐观事务实现原理
参考官网文档:https://docs.pingcap.com/zh/tidb/stable/optimistic-transaction
TiDB 加锁非原子操作,先读取,后加锁,可能存在写写冲突
其他数据库一般是在读的时候直接加锁,然后再修改。TiDB 在执行完 DML 后才能得到会修改的 key,这时才会加锁
TiDB 悲观事务实现原理
参考官网文档: https://docs.pingcap.com/zh/tidb/stable/pessimistic-transaction
部分场景下,更新前后未变化的 key 不进行加锁,系统变量
TiDB 尽量按照 FIFO 方式对解锁等待的事务唤醒,MySQL 8.0 按照 SATS(Contention-Aware Transaction Scheduling)模式唤醒
TiDB 的加锁、清锁、锁续期
等锁
悲观事务遇到锁时要等待锁释放,为了高效的实现锁等待,每个 TiKV 都有 Waiter Manager。当 Acquire Pessimistic Lock 遇到 KeyIsLocked 时,会在 Waiter Manager 里等锁释放 (commit/rollback)。
单次等锁有超时时间:
为了支持 innodb_lock_wait_timeout,TiDB 会传入 wait timeout。
TiKV 有 wait-for-lock-timeout 配置,为默认和最大的单次等锁超时时间,原因是:
若是其他事务遗留下来的锁,需要 TiDB Resolve Lock 才能清理。
若 lock 所在的 region leader 切换到其他 TiKV 上,在之前 leader 等锁的事务不会被唤醒。
v7.0 开始使用加强悲观锁唤醒模型 tidb_pessimistic_txn_fair_locking
加锁
在 prewrite 阶段的 primary key 加锁(悲观事务此时唤醒 ttl manager 开始续期,乐观事务大于 32M 此时开始唤醒 ttl manager 开始续期)
悲观锁会写到 tikv,要走 raft 消息同步流程
悲观事务 TiDB 收到来自客户端的更新数据的请求会加锁,此时的锁为悲观锁,与 prewrite 阶段加的锁类型不一样
唤醒
若多个事务等待相同锁,会先唤醒 start ts 最小的事务,其他事务会在 wake-up-delay-duration 后同时唤醒;若该锁再被释放,会唤醒 start ts 第二小的,其他事务再往后推 wake-up-delay-duration,以此类推。为了防止一直被推导致饥饿或其他问题,单次等锁时间最大仍是 wait-for-lock-timeout。
清锁
为了防止挂掉的事务写下的或者 Commit 没清理掉的 lock 一直阻塞后面事务的执行,每个 lock 都有 TTL,当事务遇到锁时会等待锁被清理或者等锁过期执行 Resolve Lock。
Resolve Lock 的流程是:查看 Primary Lock 的状态,若是已提交则把遇到的 lock 提交;若是未提交或者不存在则写入 Rollback 记录,再把遇到的 lock Rollback。
清锁只有 tidb Resolve Lock 才能清理,异常状态下,比如 tidb_server 断连,前序事务会遗留之前加的锁,在等待 wait-for-lock-timeout 后 tidb 发起 resolve lock 清理残留的锁(因为前序事务已经不会自己处理 commit 或者 rollback)
TTL manager
加锁后,TTL manager 对锁的生命周期进行管理, 主要以 txn heartbeat 的方式
lockTTL 最小 3s,最大 20s,其余情况根据参数设置及事务大小进行计算,最终值会加上事务查询时间作为浮动
每 10s 进行一次 txn heartbeat,按照 lockttl 对事务进行续期,并打印 newttl
对于悲观事务,加悲观锁的时候就会启动 ttl 续约直到 txn 结束,和事务大小无关。
乐观事务较小不会启动 ttl,由ttl-refreshed-txn-size
配置控制,默认 32MB,以该大小作为下限启动 TTL
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/26372c45dc5ee40eff8f80810】。文章转载请联系作者。
评论