本期视频 MySQL 在RR隔离级别下,能不能完全防止幻读?_哔哩哔哩_bilibili,希望大家一键三连
首先给出答案,RR隔离级别下,临界锁不能完全防止幻读。我们从什么是幻读开始讲起。
什么是幻读?
事务A进行一次查询,之后事务B插入一条数据,事务A进行第二次查询,如果两次查询到的数量不同,就是发生了幻读
。举个例子,假设有一张表user,表中有字段age,没有索引。
事务A | 事务B | |
---|---|---|
1 | begin | begin |
2 | select * from user where age > 1 | |
3 | insert into user (id, age) values (5, 35) | |
4 | commit | |
5 | select * from user where age > 1 | |
6 | commit |
如果2和5两次查出的条数不等,则说明发生了幻读。
RR隔离级别下是如何防止幻读的?
- 快照读场景
快照读
场景下,因为每次都会从快照中读取,所以每次从快照中查询到的数目都一样的。举个例子,还用上述的表。
事务A | 事务B | |
---|---|---|
1 | begin | begin |
2 | select * from user where age > 1 | |
3 | insert into user (id, age) values (5, 35) | |
4 | commit | |
5 | select * from user where age > 1 | |
6 | commit |
事务A中,2和5查询的是相同的快照,所以读取到的是相同的条数,事务B中的3插入数据成功,但是不会影响到事务A的快照,所以不会产生幻读。
- 当前读场景
当前读
场景下,因为在当前读的情况下,可能会产生临界锁,所以会阻塞其他事务的插入操作,从而避免幻读。继续借用前面的例子。
事务A | 事务B | |
---|---|---|
1 | begin | begin |
2 | select * from user where age > 1 for update | |
3 | insert into user (id, age) values (5, 35) | |
4 | commit | |
5 | select * from user where age > 1 for update | |
6 | commit |
事务A中,2查询的时候,会产生临界锁,事务B插入的时候,因为临界锁,会一直等待事务A结束后才能插入数据。所以,2和5查询的条数是相同的,不会产生幻读。
什么情况下,不能防止幻读?
直接举例
事务A | 事务B | |
---|---|---|
1 | begin | begin |
2 | select * from user where age > 1 | |
3 | insert into user (id, age) values (5, 35) | |
4 | commit | |
5 | update user set name = ‘xx’ where age = 35 | |
6 | select * from user where age > 1 | |
7 | commit |
事务A在2查询后,事务B插入一条数据,事务A的5进行了更新操作,事务6查询后,会发现事务B中插入的数据被查询到了,产生了幻读。
那么这是为什么呢?这是因为在5中,更新操作更新的是当前数据而不是快照中的数据。而更新后的数据,会写入undolog,而快照读会读取到当前事务更新的undolog,所以就会读取到新插入的数据,从而产生幻读。