写点什么

TiDB 悲观事务模式和 Mysql 的表象区别

  • 2022 年 7 月 11 日
  • 本文字数:2736 字

    阅读完需:约 9 分钟

原文来源:https://tidb.net/blog/3364b9db


【是否原创】是


【首发渠道】个人博客


【首发渠道链接】https://www.cnblogs.com/MRLL/p/15696552.html


【正文】


强烈建议先阅读完这篇文章, 本篇文章只提到了我目前遇到的情况以及衍生出来考虑的问题, 是肯定不全的~ 欢迎指正


https://zhuanlan.zhihu.com/p/87608202

前篇

tidb 在 3.0.8 之后默认开启悲观事务, 但是 autocommit 事务优先采用乐观事务提交, 翻译一下这句话就是, 如果是不手动开启事务的场景, 同时两个 insert/update 语句还是走的乐观锁机制, 就有概率触发 write conflict 触发方式如图:


SET @@tidb_txn_mode = ''; 调整本次会话为乐观模式


解决 write conflict

有两个办法


1: 在默认悲观事务的情况下开启事务 (有点绕, 说的再简单点就是 java 要加上 @Transactional 注解)


2: 开启乐观事务重试机制, 并重新建立 tidb 连接 (新建立的连接才会用上新参数)


SET GLOBAL tidb_disable_txn_auto_retry = OFF;SET GLOBAL tidb_retry_limit = 10;
复制代码


第一点很好理解, 问题出在于第二点, 因为官方原话为


TiDB 默认不进行事务重试,因为重试事务可能会导致更新丢失,从而破坏可重复读的隔离级别。

当事务中存在依赖查询结果来更新的语句时,重试将无法保证事务原本可重复读的隔离级别,最终可能导致结果与预期出现不一致。


这两句话其实很难理解, 让我们来看下图的例子

例子 1

同样要先 SET @@tidb_txn_mode = ''; 调整本次会话为乐观模式, 然后开启重试机制



最后结果也就是 t6 的更新其实并没有成功, 因为实际这个时候的 id=1 数据已经被 session B 更新为了 status=0, 在重试的时候自然匹配不上更新条件. 然后我们再回过头来看官方的原话, 因为重试事务可能会导致更新丢失. 但是这个真的是更新丢失吗, 我们回想下在如果以上操作在 mysql 下的场景, 在 t6 这一步的时候,mysql 会触发当前读, 然后直接告诉你 0 row affectied(忘记截图了, 可以自己试下), 那么在 t9 这个时间的时候,mysql 和 tidb 的表象其实是一致的, 区别在于中间更新的时候返回的影响行数

例子 2

让我们再来看一个例子


| | | || – | ———————————————————- | —————————————- || | session a | session b || t1 | begin | || t2 | | begin || t3 | update tidb set status =0 where id = 1 | || t4 | | update tidb set status =1 where id = 1 || t5 | | commit || t6 | commit | || t7 | SELECT * from tidb where id = 1 id name status 1 tidb 0 | |


可以看出在这个例子里,update 数据的时间不重要,commit 的时间才重要, 后面 commit 的数据会把先 commit 的数据进行覆盖, 对于 mysql 来说, 在 t4 这一步就会被阻断, 直到 session a 提交事务, 所以在这个场景下,tidb 和 mysql 是完全不一样的

总结下

1. 可能会造成返回的更新条数与实际情况不同, 但是最终表象会和 mysql 一致


2. 自然时间的更新顺序将没有参考意义, 数据的最新记录与 commit 时间有关, 这一点和 mysql 不一致

幻读

再额外说下幻读


可以先看下这个文章看下 mysql 的幻读


https://www.wolai.com/jtaGKJqoUusS5mmA5NqoG1


由于 tidb 没有间隙锁


所以再这个场景下,tidb 的表象也和 mysql 不一致


| | | || – | ——————————————- | ————————————————————- || | session a | session b || t1 | begin; | || t2 | | begin; || t3 | SELECT * from tidb where id >3 for UPDATE | || | | || t4 | | INSERT INTO tidb (id, name, status) VALUES (12, 'tidb', 7); || t5 | | commit; || t6 | SELECT * from tidb where id >3 for UPDATE | || | | || | 可以查询到 insert 的数据 | |


在 mysql 的场景下, 在 t4 这一步就会被阻塞, 直到 t3 加的锁被释放


其他参考文章



TiDB 和 MySQL 的锁一些分析比对 技术文章


【是否原创】是 【首发渠道】TiDB 社区 【首发渠道链接】其他平台首发请附上对应链接 【正文】 [image] 图 1 锁分类图 一、悲观锁和乐观锁 TiDB 一开始是乐观锁,但自 TiDB3.0 版本开始,支持悲观事务,并且在 3.0.8 版本开始默认使用悲观事务,支持悲观锁,查看事务:show variables like ‘%tidb_txn_mode%’; [image] 悲观锁的…



TiDB 4.0 新特性前瞻:白话“悲观锁” 原理解读


作者:Shirly 如果说在 TiDB 3.0 中,悲观锁是 “千呼万唤始出来,犹抱琵琶半遮面”。那么在 TiDB 4.0 中,悲观锁在经历了市场与时光的考验后,无论是性能还是稳定性都能够 “轻拢慢撚抹复挑,初为《霓裳》后《六幺》”,欢迎大家尝鲜与反馈。本文将从使用者的角度,介绍悲观锁的使用与注意事项,主要分为以下几方面: 白话悲观锁 TiDB 悲观锁的使用和常见现象 TiDB 悲观锁与…


发布于: 刚刚阅读数: 2
用户头像

TiDB 社区官网:https://tidb.net/ 2021.12.15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
TiDB 悲观事务模式和Mysql的表象区别_TiDB 社区干货传送门_InfoQ写作社区