在MySQL的中对于并发,锁问题总是会有很多值得讨论的地方,但是通常来说,要模拟这些锁或者一些锁的问题需要花点功夫,比如创建多个表,创建大量的数据,然后像调试钟表的秒针一样,让问题刚好复现在哪个时间点上。如果换一个角度,单表来模拟这类而是可以吗,其实是可行的。
今天简单通过单表的测试模拟死锁,事务中的隐式提交(其实可以理解是个错误),间隙锁。
初始化数据
首先的准备工作就是初始化数据,我们创建一个表测试,事务隔离级别为默认的RR。
建表语句:
创建表测试(
id int not null,
name int,
主键(id),
唯一键(名称)
)engine = innodb;
事务隔离级别:
mysql>显示'%isolation%'等变量;
+ --------------- + ----------------- +
| Variable_name | 价值|
+ --------------- + ----------------- +
| tx_isolation | REPEATABLE-READ |
+ --------------- + ----------------- +
1排(0.00秒)
除此之外就是打开两个窗口,我们简称为会话1,会话2。
模拟死锁
我们开始先模拟一下死锁问题。
会话1:
我们开启一个事务,插入一行记录,数据就选做今天的日期吧。
的MySQL>开始;
mysql>插入测试值(2017,827);
查询OK,1行受影响(0.01秒)
会话2;
mysql>插入测试值(2016,827);
这个时候会话2会阻塞,这个时候有一种特殊的情况,那就是阻塞超时,如果超时,会自动停止。
会话1:
mysql>插入测试值(2018,826);
查询正常,1行受影响(0.00秒)
可见会话1中的DML操作依旧是可以的。
会话2:
mysql>插入测试值(2016,827);
ERROR 1213(40001):尝试锁定时发现死锁; 尝试重新启动事务
如果看会话2的情况,就会发现产生了死锁。
如果要尝试事务隔离级别RC,其实表现的效果是一样的。
仔细看看这个操作的过程就会发现,还是蛮“奇怪”的,数据之间彼此没有直接的依赖关联,怎么会产生死锁,这个里面有银式锁升级,还有间隙锁的一些东西,留给大家思考吧。
模拟意料之外的事务自动提交
为了基于上面的测试数据,让两条数据成功插入,我们在会话2中结束事务。
的MySQL>提交;
然后开始做意料之外的事务自动提交测试,这一次我们在同一个会话中测试即可。问题的背景是如果我们显式声明事务,在同一会话中做了DML操作,没有提交,如果再开启一个事务,之前的事务会自动提交。
会话1:
这是基于场景1的测试之后的数据情况。
mysql> select * from test;
+ ------ + ------ +
| id | 名字|
+ ------ + ------ +
| 2018年| 826 |
| 2017年| 827 |
+ ------ + ------ +
2行(0.00秒)
我们显式声明一个事务。
mysql>开始;
查询正常,0行受影响(0.02秒)
然后插入一条记录,重新给一个日期。
mysql>插入测试值(2019,825);
查询正常,1行受影响(0.00秒)
这个时候没有提交,我们在当前会话中重新再开启一个事务。
mysql>开始;
mysql>插入测试值(2015,830);
这个时候如果在会话2中查看,其实会发现,事务已经帮你提交了。
mysql> select * from test;
+ ------ + ------ +
| id | 名字|
+ ------ + ------ +
| 2018年| 826 |
| 2017年| 827 |
+ ------ + ------ +
2行(0.00秒)
在会话1我们继续回滚事务,会发现于事无补。
mysql> rollback;
查询OK,0行受影响(0.01秒)
这个时候数据已经自动提交了一部分。
mysql> select * from test;
+ ------ + ------ +
| id | 名字|
+ ------ + ------ +
| 2018年| 826 |
| 2017年| 827 |
+ ------ + ------ +
2行(0.00秒)
间隙锁测试
上面的测试场景其实还是多多少少都有些关联,其中第一个场景和间隙锁也有关系,我就简单用单表模拟一下间隙锁。
首先还是保证事务隔离级别是RR,因为间隙锁是RR隔离级别特供,RC中就没有间隙锁这样的定制,在并发场景中还是有不小的影响。我们来看看效果。
会话1:
mysql>开始;
查询正常,0行受影响(0.00秒)
指定数据范围,然后显示声明。
mysql> select test from test where id <2019 lock in share mode;
+ ------ +
| id |
+ ------ +
| 2018年|
| 2017年|
+ ------ +
2行(0.00秒)
会话2:
会话2中也开启一个事务,插入一条记录。结果就被阻塞了。
mysql>开始;
查询正常,0行受影响(0.00秒)
mysql>插入测试值(2016,829);
ERROR 1205(HY000):超出锁定等待超时; 尝试重新启动事务
直到事务超时才作罢。
原文:http ://blog.itpub.net/23718752/viewspace-2144203/