MySQL锁详解

MySQL锁详解


理论是灰色的,实践之树长青 ——恩格斯


官方文档介绍

在MySQL官方文档中,介绍了InnoDB的七种锁:

  1. 共享锁(S锁)与排他锁(X锁);
  2. 意向锁(Intention Locks);
  3. 记录锁(Record Locks);
  4. 间隙锁(Gap Locks);
  5. 临键锁(Next-Key Locks);
  6. 插入意向锁(Insert Intention Locks);
  7. 自增锁(Auto-inc Locks);

但是上述的锁分类并不是从同一个维度进行划分的,所以在这里我们采用另一种划分方式,从几个维度来划分锁。

  • 按锁的粒度来分:表锁、行锁、间隙锁(Gap Lock);
  • 按锁的模式来分:共享锁、排他锁、意向锁;

按照两个维度叉乘我们可以得到一下的锁:

表锁 行锁 间隙锁
共享(S) 表共享锁 行共享锁 插入意向锁、临键锁、插入意向锁
排他(X) 表排他锁 行排他锁
意向共享(IS) 表意向共享锁 X
意向排他(IX) 表意向排他锁 X
AI(Auto-inc Lock) 自增锁 X

各类锁详解

1、表(S锁、X锁)、行(S锁、X锁)

共享锁(S)和排他锁(X)是读写锁的另外一种叫法,共享锁 即“读锁”,读和读之间可以并发;排他锁就是写锁,读和写之间不能并发,写和写之间也不能并发。

InnoDB通常加锁的粒度是行,所以有对应的行共享锁、行排他锁,但有些场景会在表这个粒度加锁,比如DDL语句。 表和行两个粒度的共享锁、排他锁都比较容易理解,而下面要讨论 的意向锁、自增锁、Gap锁、插入意向锁等,需要结合特定的场景才能知道其用途。

2.意向锁(IS锁、IX锁)

有了共享锁和排他锁,为什么还会有“意向锁”呢?假设事务A给表 中的某一行记录加了一行排他锁,现在事务B要给整张表加表排他锁, 事务B应该怎么处理呢?显然事务B加锁不会成功,因为表中的某一行 正在被A修改。但事务B要做出这个判断,它需要遍历表中的每一行, 看是否被加了锁,只要有任何一行加了行排他锁,就意味着整个表加了 表排他锁。

很显然这种判断方法的效率太低,而意向锁就是为了解决这个锁的 判断效率问题产生的。意向锁是专门加在表上,在行上面没有意向锁。 一个事务A要给某张表加一个意向S锁,是“暗示”接下来要给表中的某一 行加行S锁;一个事务A要给某种表加一个意向X锁,是“暗示”接下来要 给表中的某一行加行X锁。反过来说,一个事务要给某张表的某一行加S锁,必须先获得整张表的IS锁;要给某张表的某一行加X锁,必须先获 得整张表的IX锁。

有了这种“暗示”,事务 B 要给整张表加表排他锁,就不用遍历所有 记录了。只要看一下这张表有没有被其他事务加IX锁或者IS锁,就能做 出判断。也正因为是“暗示”,是一种很“弱”的互斥条件,所以所有的IX 锁、IS锁之间都不互斥,IX锁、IS锁只是为了和表共享锁、表排他锁进 行互斥。

⚠️注意:
通过上面的分析可以得出,意向锁实际上是表(共享锁、排他锁)和行(共享锁、排他锁)之间的桥梁,通过意向锁来串起两个不同粒度(表、行)的锁之间如何做互斥判断。

3.AI(Auto-inc Locks)

自增锁是一种表级别的锁,专门针对AUTO_INCREMENT的列。

为什么会需要这种锁呢?看下面的事务:

start transaction
	insert t1 values(xxx,xxx,xxx)
	insert t2 values(xxx,xxx,xxx)
	insert t3 values(xxx,xxx,xxx)
commit

假设表t1中有某一列是自增的,连续insert两条记录,再select出来,自增的一列的取值应该也是连续的,比如第一次insert该自增列的 取值是6,则第二次insert该自增列的取值应该是7;但如果不加AI锁, 可能别的事务会在这两条insert中间插入一条记录,那么该事务第二次 insert的记录的自增列取值可能就不是7,而是8。然后select出来后,一 条记录的自增列取值是6,另一条是8,对于该事务来说很奇怪,明明连 续插入了两条,自增列却不是连续递增,不符合AUTO_INCREMENT原 则。

4.间隙锁(Gap Lock)、临键锁(Next-Key Lock)和插入意向 锁(Insert Intension Lock)

除锁表、锁行两种粒度外,还有第三种:锁范围,或者叫锁Gap。 锁Gap是和锁行密切相关的,Gap肯定建立在某一行的基准之上,所以 往往又把锁Gap当作锁行的不同算法来看待:

  • 间隙锁(Gap Lock):只是锁一个范围,不包括记录本身, 也是一个开区间,目的是避免另外一个事务在这个区间上插入新记录。
  • 临键锁(Next-Key Lock):Gap Lock与Record Lock的综合不 仅锁记录,也锁记录之前的范围。
  • 插入意向锁(Insert Intension Lock):插入意向锁也是一种 Gap锁,专门针对Insert操作。多个事务在同一索引、同一个范围区间内 可以并发插入,即插入意向锁之间并不互相阻碍。

锁Gap的各种算法实际很复杂,需要结合InnoDB源码仔细分析。这 里主要说明两点:

  • 第一,是否加Gap锁和事务隔离级别密切相关。所以要锁Gap,一 个主要目的是避免幻读。如果事务的隔离级别是RC,则允许幻读,不 需要锁范围。
  • 第二,锁Gap往往针对非唯一索引,如果是主键索引,或者非主键 索引(但是唯一索引),每次修改可以明确地定位到哪一条或者哪几条 记录,也不需要锁Gap。

总结

具体到不同类型的SQL语句、不同的事务并发场景、不同的事务隔 离级别、不同的索引类型,加的锁都可能不一样。在实践中,还要借助 数据库的分析工具查看写的SQL语句到底被加了什么锁,而不能武断地 推测。

你可能感兴趣的:(数据库,mysql,innodb,数据库)