我又看了一遍 tidb 配套的设计:
https://github.com/pingcap/tidb/pull/37518/files
感觉大部分看懂了。
但是对这个流程好像还是不太了解:
When a key is locked with conflict, the current statement becomes executing at a different snapshot. However, the statement may have already read data in the expired snapshot (as specified by the
for_update_ts
of the statement). In this case, we have no choice but retry the current statement with a newfor_update_ts
.
这里说,由于 sql 语句很可能已经读了旧版本 snapshot 的数据,因此我们需要重试读取最新的 for_update_ts 数据。
可能我对 tidb 的悲观锁加锁的场景了解有限,我理解的场景例如:
select * from t where id=1 for update; (表中不存在唯一索引)
悲观事务中很可能由于其他事务原因导致这个 select 请求阻塞,如果使用 fair locking 的话,tikv 在其他事务 commit 的时候,顺利拿到了 id=1 这个行锁,并且向 tidb 返回了 id=1 的最新数据以及其 commit_ts.
这个流程中好像并不需要重试 select for update 语句了。
insert 语句大概率应该不需要,大部分场景下 insert 应该不需要查询。
那么什么样的 sql 语句,在悲观事务中,即使 fair locking 成功了,也需要再次重试呢?
对于 delete 语句、update 语句,例如 delete from t where k=2; update t set a=3 where k=2;
可能由于阻塞过程中,其他事务提交了 k=2 的新数据,所以 tidb 需要重试,重新查询新的 snapshot 下所有 k=2 的 rowid,然后一一加锁。
对于存在唯一索引的表数据,
select * from t where indexKey=1 for update; 或
select * from t where id=1 for update;
不太清楚是先 seek 行数据再加 index 锁和 rowid 锁,还是先加 index 锁和 rowid 锁再 seek 行数据。
如果是前者的话,那的确需要再重试 seek 一下
如果是后者的话,好像也没必要重试
不知道我自己猜测的场景对不对