MySQL独占间隙锁为什么会互相兼容?
如标题疑惑,对于下面SQL:
select id from t order where order_no = 1007 for update
假设这里的order_no是二级索引,并且表中只有
(id,order_no):(1,1001)、(2,1002)、(3,1003)、(4,1004)、(5,1005)、(6,1006)这六个数据。
那刚刚A事务执行的select id from t order where order_no = 1007 for update 语句就会在order_no索引上加一个(1006,+∞)区间的独占间隙锁(注意,这里是RR级别时才会加独占间隙锁锁,如果级别为读已提交RC,那找不到对应的记录时,就会自动释放锁,这也就是为什么RC级别性能好但是无法解决幻读的原因)
但是,如果此时B事务也执行select id from t order where order_no = 1008 for update 语句,也会持有(1006,+∞)区间的独占间隙锁
那,独占体现在哪里呢,为什么A、B事务都可以同时持有独占间隙锁?
独占间隙锁的独占:
不是排斥其他事务的间隙锁,而是排斥其他事务的 插入操作(插入意向锁)。
多个事务可以同时持有同一个间隙的锁,但它们会互相阻塞插入操作。
那么,插入意向锁是什么?
插入意向锁是一种特殊的间隙锁,表示“准备在这个间隙插入一条记录”,如果A事务想要在(1006,+∞)区间插入一条数据,但是对应的这个间隙被B事务或其他事务持有这个独占间隙锁,那插入就会阻塞等待。
如果A事务想要插入一条数据,这个间隙的独占间隙锁被当前自己这个事务持有了,那插入就不会被阻塞(别人持有间隙锁就阻塞,自己持有间隙锁或者没有间隙锁,那插入就不会阻塞)
这也是为什么小林大佬的这幅图中会出现死锁的现象,原因就是独占间隙锁是可以多事务持有的(猜测是为了更好的并发性能吧),但是插入意向锁对应间隙的间隙锁又不能被其他事务持有,这才导致了死锁发生(互相等待释放间隙锁)
Q:从理论学习来看,应该是写写互斥、写读互斥、读读不互斥,但是这里看起来像独占间隙锁被多个事务持有,那独占间隙锁和共享间隙锁,有什么不一样吗?
A:
这里MySQL的设计是一个伪互斥,其实一个区间如果申请了独占间隙锁lock1,在这个区间申请独占间隙锁lock2还是会被阻塞的,只不过是采用A事务申请被持有lock1的引用,B事务也持有这个lock1的引用这种方式实现的。
因此,独占间隙锁和共享间隙锁,其他从功能来看都是一样的,都是加了间隙锁,而防止插入操作的发生,从而避免了幻读。
唯一不一样可能就是语义了,独占更强调是"我要修改数据,别打扰",共享更强调"我要读数据,别打扰"