背景
在 v6.0.0 版本,针对悲观事务引入了内存悲观锁的优化(in-memory lock),从压测数据来看,带来的性能提升非常明显。
TiDB 事务模型从最初的乐观事务到悲观事务;在悲观事务上,又针对悲观锁进行的 ”pipelined 写入“ 和 ”in-memory lock“ 优化,从功能特性上可以看出演进过程。
乐观事务
乐观事务在提交时,可能因为并发写造成写写冲突,不同设置会出现以下两种不同的现象:
-
关闭乐观事务重试,事务提交失败:也就是执行 DML 成功(不会被阻塞),但是在执行 commit 时候失败,表现出与 MySQL 等数据库不一致的行为。
-
开启乐观事务重试,自动重试后返回成功,但是因为重试 DML 使用的 start_ts 是重新获取的,不是事务开始的 start_ts,也就是说实际执行结果相当于同一个事务中读和写是使用不同的 start_ts,执行结果可能跟预期不一致。
悲观事务
针对乐观事务存在的问题,悲观事务通过在执行 DML 过程中加悲观锁,来达到与传统数据库的行为:
-
并发执行 DML,对同一行数据进行更改,先执行者会加悲观锁,后执行者被锁阻塞
-
悲观锁避免了写冲突的问题,使得 commit 顺利完成
悲观事务写入悲观锁,相对乐观事务带来以下开销:
-
悲观锁写入 TiKV,增加了 RPC 调用流程并同步等待悲观锁写入成功,导致 DML 时延增加
-
悲观锁信息会通过 raft 写入多个副本,给 TiKV raftstore、磁盘等带来处理压力
pipelined
针对悲观锁带来的时延增加问题,在 TiKV 层增加了 pipelined 加锁流程优化,优化前后逻辑对比:
-
优化前:满足加锁条件,等待 lock 信息通过 raft 写入多副本成功,通知 TiDB 加锁成功
-
pipelined :满足加锁条件,通知 TiDB 加锁成功、异步 lock 信息 raft 写入多副本(两者并发执行)
异步 lock 信息 raft 写入流程后,从用户角度看,悲观锁流程的时延降低了。
in-memory
pipelined 优化只是减少了 DML 时延,lock 信息跟优化前一样需要经过 raft 写入多个 region 副本,这个过程会给 raftstore、磁盘带来负载压力。
内存悲观锁针对 lock 信息 raft 写入多副本,做了更进一步优化,总结如下:
-
lock 信息只保存在内存中,不用写入磁盘
-
lock 信息不用通过 raft 写入多个副本,只要存在于 region leader
-
lock 信息写内存,延迟相对于通过 raft 写多副本,延迟极小
从优化逻辑上看,带来