行锁的三种算法
一、Record Lock:单个记录上的锁
Record Lock总是会锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。
二、Gap Lock:间隙锁
设计目的:是为了解决Phantom Problem(幻象/幻读),利用这种锁技术,锁定的不是单个值,而是一个范围。
三、Next-Key Lock:Gap Lock+Record Lock
Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在此种算法下,InnoDB对于行的查询都是采用这种锁定算法。
例如:
有10、11、13、20这四个值,那么索引可能被Next-Key Locking的区间为:
1、(-∞,10]
2、(10,11]
3、(11,13]
4、(13,20]
5、(20,+∞)
除了Next-Key Locking,还有Previous-Key Locking,如上面的例子,可锁定的区间为:
1、(-∞,10)
2、 [10,11)
3、 [11,13)
4、 [13,20)
5、 [20,+∞)
然而,当查询的索引含有唯一属性时,InnoDB存储引擎对Next-Key Lock进行优化,将其降级为Record Lock,即仅锁住索引本身,而不是范围。如表中有1,2,5的id数据:
会话A首先对a=5进行X锁。而由于a是主键且唯一,因此锁定的仅是5这个值,而不是(2,5)的范围,这样在B中插入值4不会阻塞,可以立即插入并返回。即锁定由Next-Key Lock算法降级为Record Lock,从而提高了并发性。
Next-Key Lock降级为Record Lock仅在查询的列是唯一索引的情况下。
若是辅助索引,则情况会完全不同如:
CREATE TABLE z(a INT,b INT,PRIMARY KEY(a),KEY(b))
INSERT INTO z SELECT 1,1;
INSERT INTO z SELECT 3,1;
INSERT INTO z SELECT 5,3;
INSERT INTO z SELECT 7,6;
INSERT INTO z SELECT 10,8;
表z的b列是辅助索引,若在会话A中执行下面的SQL:
SELECT * FROM z WHERE b=3 FOR UPDATE;
这是Sql语句通过索引列b进行查询,因此其使用传统的Next-Key Locking加锁,并且由于两个索引,其需要分别进行锁定。对于聚集索引,其仅对a等于5的索引加上record Lock。对于辅助索引,其加上的是Next-Key Lock,锁定的范围是(1,3),需要注意的是,InnoDB存储引擎还会对辅助索引下一个键值加上gap lock,即还有一个辅助索引的范围为(1,6)的锁,因此,在B会话中运行下面的Sql语句,都会被阻塞:
SELECT * FORM z WHERE a=5 LOCK IN SHARE MODE;
INSERT INTO z SELECT 4,2;
INSERT INTO z SELECT 6,5;
而执行下面的语句,不会阻塞:
INSERT INTO z SELECT 8,6;
INSERT INTO z SELECT 2,0;
INSERT INTO z SELECT 6,7;
在默认的事务隔离级别下,即REPEATABLE READ下,InnoDB存储引擎采用Next-Key Locking机制来避免Phantom Problem(幻象问题)