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
事务的隔离级别是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
相关文章推荐
- [Mysql]——通过例子理解事务的4种隔离级别
- Mysql加锁过程详解(6)-数据库隔离级别(2)-通过例子理解事务的4种隔离级别
- SQL事务的四种隔离级别和MySQL多版本并发控制
- [Mysql]——通过例子理解事务的4种隔离级别
- Mysql加锁过程详解(6)-数据库隔离级别(2)-通过例子理解事务的4种隔离级别
- mysql binlog格式与事务级别read committed的关系
- MYSQL下read committed 和repeatable read级别下一致性非锁定读笔记+实测
- mysql innoDB的REPEATABLE READ MVCC事务实现方式
- [Mysql]——通过例子理解事务的4种隔离级别
- [Mysql]——通过例子理解事务的4种隔离级别(转)
- Mysql事务和隔离级别(read committed, repeatable read)
- Mysql——通过例子理解事务的4种隔离级别
- [Mysql]——通过例子理解事务的4种隔离级别(转)
- [Mysql]——通过例子理解事务的4种隔离级别
- 再谈mysql repeatable事务级别适用场景与SQL Server的snapshot隔离级别
- MySQL存储过程例子,包含事务,参数,嵌套调用,游标,循环等
- MySQL存储过程例子,包含事务,参数,嵌套调用,游标,循环等
- 解读MySQL事务的隔离级别和日志登记模式选择技巧
- MySQL存储过程例子,包含事务,参数,嵌套调用,游标,循环等
- 事务传播级别隔离级别以及高并发下的应用经验