通过上一篇文章https://www.jianshu.com/p/55f79dc4b289,分析了RR级别下并没有彻底解决幻读的问题。这是由于事务执行过程中,允许别的事务插入新数据;而当前事务的更新操作(update,delete)都是当前读。如果要彻底幻读问题,就得在事务查询执行中,不允许别的事务插入影响该查询结果的数据。例如:事务A查询id>10的数据,那么事务B就不允许插入id>10的数据。这样的实现需要锁来保证。下面介绍一下:Gap锁。
Gap锁
范围分析
假设有一张表t,主键:id,普通索引列:col1,索引idx_col1
-
锁的是索引数据,不是行数据
事务a:
select * from t where col1=30 for update;
+----+------+------+
| id | name | col1 |
+----+------+------+
| 10 | a2 | 30 |
| 51 | lll | 30 |
+----+------+------+
2 rows in set
锁住了索引数据col1=30;
事务b:
update t set col1='51' where id=51; 阻塞(该行数据的索引列col数据是30,被事务a锁住了)
update t set col1='30' where id=50;阻塞(索引列col数据为30已经被事务a锁住了)
update t set col1='70' where id=50;不阻塞 -
唯一索引
唯一索引来锁定唯一行来锁定行的语句,不需要间隙锁定;此时的锁是行锁
事务a:select * from t where id=30 for update;
+----+------+------+
| id | name | col1 |
+----+------+------+
| 30 | b2 | 50 |
+----+------+------+
1 row in set
事务b:
insert into t(id,name,col1) values(32,'1',31);
Query OK, 1 row affected
update t set col1='51' where id=30;阻塞了(事务a上了行锁)范围锁定行
1.select * from t where id>30 for update;
| id | name | col1 |
+----+------+------+
| 40 | n40 | 60 |
| 50 | 30 | 70 |
| 51 | lll | 30 |
+----+------+------+
范围:[30,51)、[51,正无穷大),
2.select * from t where id>30 and id<40 for update;
范围:[30,30)、(30,40)、(40,40]
3.select * from t where id between 30 and 40 for update;
范围:[20,30)、(30,40)、(40,50]
4.select * from t where id<30 for update;
范围:(-无穷大,30)、(30,30]