强人“锁”难,MySQL到底有多少锁?( 二 )


Intention locks do not block anything except full table requests (for example, LOCK TABLES … WRITE). The main purpose of intention locks is to show that someone is locking a row, or going to lock a row in the table.
意向锁不会阻止任何其他请求,除了(锁定)全表请求(例如LOCK TABLES ... WRITE)外 。意向锁定的主要目的是:声明已经有事务正在锁定表中的行,或者即将锁定表中的行 。
这句话透露出了意向锁虽然是表锁,但是和行锁是完全兼容的(包括共享锁和排它锁) 。但是声明表中的行正在被锁定或者即将被锁定,有什么用呢?
假设事务A给表中某一行加了排它锁,而事务B想给这个表加一个全表的排他锁,这时候事务B就需要判断当前表中到底有没有排他锁,如果有的话,是不能成功加上去表的排它锁的 。此时,如果没有意向锁,事务B只能遍历整个索引去判断,这样无疑是低效的 。为了解决这个问题,MySQL引入了意向锁 。当事务A给某一行加了排它锁或者共享锁后,会分别在表上加意向排它锁(IX)或者意向共享锁(IS),这时,事务B就可很轻松的判断当前表是否有行锁,这也就是前文所说的:为了使在多个粒度级别上的锁定变得切实可行,InnoDB实现了意图锁 。
理解了意图锁的作用,再来看看上面的表格 。S锁和X锁之间的兼容性前文已经理清了,还剩下IX和IS之间以及S/X和IS/IX的兼容性 。
由于某一行被加了S锁或者X锁后,表上都会加上对应的IS锁和IX锁 。另一个事务想锁住另一条记录,也得加上对应的IS锁或者IX锁,所以IS和IS、IS和IX以及IX和IX必定是兼容的,不然整个表最多只能上一个行锁 。
至于S锁、X锁和IS锁、IX锁的兼容性则需与分情况讨论了 。当整个表上了X锁之后,再也不能上别的X锁或者S锁了,所以X锁和IS、IX都是冲突的 。当整个表上了S锁之后,不能再上X锁了,但是还可以上S锁,所以S锁和IX锁是冲突的,但是和IS锁是兼容的 。
这样理解了之后,再看上面的那个表格,似乎也变得有规律了 。
间隙锁(Gap Locks)间隙锁锁定的是索引记录之间的间隙,或者在第一个或最后一个索引记录之前的间隙 。例如SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE,可以防止其他事务将t.c1为15的记录插入表中,因为这两个值之间的间隙是被锁定的 。
间隙可能跨越单个索引值,多个索引值,甚至为空 。
间隙锁的唯一目的是防止其他事务在间隙中插入数据 。间隙锁可以共存 。一个事务执行的间隙锁,不会阻止另一事务对相同的间隙进行间隙锁定 。
Next-Key LocksNext-Key Locks是索引记录上的行锁和索引记录之前的间隙上的间隙锁的组合 。如果一个session在索引中的记录R上具有共享或排他锁,则另一session不能按照索引顺序在R之前的间隙中插入新的索引记录 。
默认情况下,InnoDB设置的事务隔离级别是REPEATABLE READ 。在这种情况下,InnoDB使用Next-Key Locks进行搜索和索引扫描,这可以防止幻读(虚读) 。关于MySQL隔离级别相关的问题,请参考:面试官:MySQL事务是怎么实现的
插入意图锁(Insert Intention Locks)插入意图锁是在行插入之前,通过INSERT操作设置的间隙锁的一种类型 。如果多个事务想要在同一个间隙中插入不同的值(也就是插入的位置不同),则这多个事务均不会被阻塞 。假设有索引记录,其值分别为4和7 。单独的事务分别尝试插入值5和6,在获得插入行的排他锁之前,每个事务都使用插入意图锁来锁定4和7之间的间隙,但不会互相阻塞,因为插入的行是无冲突的 。
自增锁(AUTO-INC Locks)AUTO-INC锁是一种特殊的表级锁,由事务插入具有AUTO_INCREMENT列的表中获得 。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务都必须等待这个事务在该表中进行插入,以便第一个事务插入的行接收连续的主键值 。
MyISAM中的锁相比之下MyISAM中的锁就简单多了,因为MyISAM只支持表锁 。并且共享锁和排它锁也满足如下关系
强人“锁”难,MySQL到底有多少锁?

文章插图
 
死锁前文提到InnoDB行锁可能是出现死锁,死锁是一个计算机领域的概念,而不是数据库特有的,所以死锁的概念是通用的 。这里演示下MySQL行锁导致的死锁,这也是官网上给出的例子
首先准备数据
## 创建表CREATE TABLE t (i INT) ENGINE = InnoDB;## 新增数据INSERT INTO t (i) VALUES(1);具体操作的时间线如下
强人“锁”难,MySQL到底有多少锁?


推荐阅读