关于Mysql事务REPEATABLE_READ隔离级别下多版本并发控制(MVCC)重复读产生的坑的你踩过么?

前几天在系统中开发一个活动, 使用了行级锁,但是却在并发测试下出现了重大的Bug,话不多说, 直接开始.

事务的隔离级别是Mysql默认的REPEATABLE_READ.


Time1:事务A(Ta)锁定表t_1中id=3的记录
Time2: 事务A
查询表t_3中的数据.

Time3: 事务B(Tb)也去尝试锁定表t_1中id=3的记录,  没有获得锁, 线程等待.

Time4:事务B查询表t_3中的数据.
Time5: Ta查询表t_2中status=0的前一条记录, id=10,并修改该条记录的stauts=1.
Time6: Ta提交事务.
Time7: Tb获得记录锁,
Time8: Tb查询表t_2中status=0的前一条记录, id=10.(这是个问题,为什么在Tb事务中
能获得到这条记录?)
Time9: Tb修改表t_2中id=10这条记录的status=1时, 没有修改到.因为update语句执行返回的条数为0.抛出异常.
Time10: Tb回滚. 

t_2表的查询语句:select id, num fromt_2 where status=0 and id=10
t_2表的更新语句:update t_2 set status=1 where status=0 and id=
10


整个测试出现的问题就是Ta已经修改了t_2表中id=10的这个记录的status=1,并Commit,但是在Time7时,但是Tb查询t_2.id=10这条记录时,该条记录的status字段的值仍然是0,这是在Tb事务第一次查询该t_2表.

后来我将事务隔离级别换成READ_COMMITED,测试正常了. 问题看似是解决了,但是在REPEATABLE_READ级别中出现这种情况的原因还没有找到,这样也无法避免以后再次踩坑.就在网上找资料,发现Mysql的多版本并发控制(MVCC),如果有道友不了解的,可以打开链接查看.这里仅简单的说明一下:

通过保存数据在某个时间点的快照来实现的。在每个事务开启时,都会有一个事务版本号,SELECT时只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,只么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。

测试很久遍之后, 我把两个事务内执行的顺序改变了一下:

Time1:事务A(Ta)锁定表t_1中id=3的记录

Time2: 事务A查询表t_3中的数据

Time3: 事务B(Tb)也去尝试锁定表t_1中id=3的记录,  没有获得锁, 线程等待.

Time5: Ta查询表t_2中status=0的前一条记录, id=10,并修改该条记录的stauts=1.
Time6: Ta提交事务.
Time7: Tb获得记录锁
Time4:事务B查询表t_3中的数据.
Time8:Tb查询表t_2中status=0的前一条记录, id=10.(这是个问题,为什么在Tb事务中能获得到这条记录?)
Time9: Tb修改表t_2中id=10这条记录的status=1时, 没有修改到.因为update语句执行返回的条数为0.抛出异常.
Time10: Tb回滚. 

发现只在事务开启后,第一次跟数据库的交互去获取记录的锁,这时就可以正常.

出现并发问题的:

不会出现并发问题的:



猜测:

Mysql数据库的事务隔离级别REPEATABLE_READ中,事务开始后,跟数据库第一次交互后,SELECT语句就只能查找到这个事务之前并且不在该事务内改变的数据.


如果有不同见解,欢迎指出.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值