MySQL锁机制深入理解(2)

即Record lock和Gap lock的合体。例如SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;会在[10,20]之间的c1 index record上加lock_mode为X的next-key lock,也就是说会在[10,20]之间的所有存在的index record上加X模式的record lock,同时也会用X模式的gap锁锁定不存在的index record防止幻读,这两种锁加起来就称作next-key lock。

如果使用的索引是唯一索引,那么不加next-key lock的,只加record lock。

再次提醒的是next-key lock其实并不存在而是Record lock和Gap lock的合体,show engine innodb status\G显示的结果也都是用Record lock来展示的,不过展示出的数目比较诡异看不懂源码的话不建议深究,这点比Oracle和Sqlserver差太远。这里我就要顺带吐槽一下官网手册了,毕竟是开源DB,一些前后矛盾和明显有歧义的解释也是让人很无奈。

4.插入意向锁(Insert Intention Locks)

这个锁也是一个InnoDB的奇葩例子,不知道大家发现没InnoDB在谈IX IS还有行锁这些锁的时候基本不用insert语句来举例,这点如果是熟悉Oracle和SQL Server的人就会很困惑,因为增删改全都是DML语句,大家加锁机制基本相似的,无非就是表级意向锁+页级or行级锁的套路,但是InnoDB不是这样!!!insert语句和delete、update完全不是一路人!!

这个锁用于表明:只要不是插入相同的index record,多个事务向同一个gap插入记录是不会阻塞的。

插入意向锁其实是行级别的一种意向gap锁,既然有意向两字那么可以认定就是用于检测锁冲突的,是为在行级别获取X模式的record lock锁提前做检测。

用一个例子来解释更为明了:

--会话A执行:
CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
INSERT INTO child (id) values (90),(102);
START TRANSACTION;
SELECT * FROM child WHERE id > 100 FOR UPDATE;
--会话B执行:
INSERT INTO child (id) VALUES (101);

可以看到会话B被阻塞了,而show engine innodb status\G看到的锁等待如下:

MySQL锁机制深入理解

即insert语句想在(90,102)的gap上加个lock_mode=X的gap锁,也就是Insert Intention Lock,但是会话A的select for update语句已经在(100,102)的gap上添加了X模式的gap锁,这是一个与(90,102)不同但被包含在内的gap,于是被阻塞无法获取X模式的Insert Intention Gap Lock。

三、总结

MySQL的锁机制基本就如上所示了,但是了解InnoDB锁只是初步的,还必须结合事务隔离级别的概念去判断各种SQL的具体加锁机制,因为事务隔离级别会影响SQL的默认加锁模式。

MySQL的事务隔离级别定义也是遵循ANSI SQL92标准的,不过但凡是家数据库厂商都会说自己遵循SQL92标准,而事实是早已加料加的面目全非。当然这全都是为了能够提供更好的并发性能。例如Oracle也说自己遵循SQL92标准,结果四大隔离级别只支持2个,SQL Server也说自己支持,结果又多造出来2个事务隔离级别。

同样的MySQL也提供了4大基本的事务隔离级别,不同的隔离级别下加锁机制区别很大,将在另一篇笔记中详述。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/db08aa6e713b76ffb669bf88e21525c9.html