您的位置:首页 > 数据库 > MySQL

REPEATABLE_READ事务级别MYSQL并发小例子

2017-06-11 11:53 239 查看
前几天在系统中开发一个活动, 使用了行级锁,但是却在并发测试(压力测试和并发测试非常重要)下出现了重大的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回滚. 

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

出现并发问题的:

不会出现并发问题的:

文章来源:http://blog.csdn.net/mr_rain/article/details/51859546
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: