译:InnoDB 锁

什么是死锁

不同的事物无法继续,因为每个事物都持有另一个需要的锁,两个事物都在等待资源可用,导致它们都不会释放它锁拥有的锁。
参考地址1
参考地址2
举个例子:

// 首先,客户端A创建一个包含一行的表,然后开始一个事务。在事务中,A通过S在共享模式中选择它来获取该行的 锁定:

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (1.07 sec)

mysql> INSERT INTO t (i) VALUES(1);
Query OK, 1 row affected (0.09 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
+------+
| i    |
+------+
|    1 |
+------+
// 接下来,客户端B开始一个事务并尝试从表中删除该行:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> DELETE FROM t WHERE i = 1;
// 删除操作需要X锁定。无法授予S锁定,因为它与客户端A持有的锁不兼容 ,因此请求将进入行和客户端B块的锁定请求队列。


// 最后,客户端A还尝试从表中删除该行:
mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction
// 此处发生死锁,因为客户端A需要 X锁定才能删除该行。但是,无法授予该锁定请求,因为客户端B已经有X锁定请求并且正在等待客户端A释放其S锁定。由于B先前要求锁定,因此SA所持有的锁也不能 升级 X为X锁。
结果, InnoDB为其中一个客户端生成错误并释放其锁定。客户端返回此错误:
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

mysql 8.0 有哪些锁

  • 共享锁和排它锁(Shared and Exclusive Locks)
    InnoDB实现标准的行级锁,其中有两种类型的锁shared(S)和exclusive(X)锁

    • 一个共享锁允许持有锁读取行的事务
    • 一个排它锁允许持有锁,更新或者删除行的事务

如果事务在行上T1持有一个shared(S)锁r,那么来自某个不同事务T2 的对行锁的请求r将按如下方式处理:

由A请求T2用于 S锁可以立即被授予。其结果是,无论是T1与T2 持有S的锁r。

通过请求T2一个 X锁不能立即授予。

如果事务在行上T1持有exclusive(X)锁r,则不能立即授予来自某个不同事务T2的锁定任何类型的锁的请求r。相反,事务T2必须等待事务T1释放其对行的锁定r。

  • 意向锁 (Intention Locks)

InnoDB支持多粒度锁定,允许行锁和表锁共存。例如,诸如 在指定表上LOCK TABLES ... WRITE进行独占锁定(X锁定)之类的语句 。要在多个粒度级别实现锁定,请InnoDB使用 意图锁定。意图锁是表级锁,它指示事务稍后对表中的行所需的锁(共享或独占)类型。意图锁有两种类型:

的意图共享锁(IS)指示一个事务打算设置一个共享 上各个行锁定在表中。

的意图独占锁(IX)指示一个事务打算设定各行的排他锁在表中。

一种适用于表的锁,用于指示事务要在表中的行上获取的锁类型 。不同的事务可以在同一个表上获取不同类型的意图锁,但是获取表上的意图独占(IX)锁的第一个事务会阻止其他事务获取表上的任何S或X锁。相反,第一个获得意图的交易是 共享的(IS)锁定表阻止其他事务获取表上的任何X锁。两阶段进程允许按顺序解析锁定请求,而不会阻塞锁定和兼容的相应操作。

例如,SELECT ... FOR SHARE设置IS锁定并 SELECT ... FOR UPDATE设置IX锁定。

意图锁定协议如下:

在事务可以获取表中某行的共享锁之前,它必须首先IS在表上获取锁或更强。

在事务可以获取表中某行的独占锁之前,它必须首先获取IX 表上的锁。

表级锁定类型兼容性总结在以下矩阵中。

- X IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible COmpatible Compatible

如果请求事务与现有锁兼容,则授予锁,但如果它与现有锁冲突则不会。事务等待,直到释放冲突的现有锁。如果锁定请求与现有锁定冲突且无法授予,因为它会导致 死锁,则会发生错误。

意图锁定不会阻止除完整表请求之外的任何内容(例如,LOCK TABLES ... WRITE)。意图锁定的主要目的是显示某人正在锁定行,或者要锁定表中的行。

  • 记录锁 (Record Locks)

记录锁定是对索引记录的锁定。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 可以防止从插入,更新或删除行,其中的值的任何其它交易t.c1是 10。

即使定义了没有索引的表,记录锁也始终锁定索引记录。对于此类情况,请 InnoDB创建隐藏的聚簇索引并使用此索引进行记录锁定。请参见 第15.6.2.1节“聚簇和二级索引”。

记录锁的事务数据SHOW ENGINE INNODB STATUS与 InnoDB监视器 输出中的以下内容类似:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` 
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;
  • 区间锁\间隙锁(Gap Locks)

间隙锁定是锁定索引记录之间的间隙,或锁定在第一个或最后一个索引记录之前的间隙。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;阻止其他事务将值15插入列t.c1,无论列 中是否已存在任何此类值,因为该范围中所有现有值之间的间隙都已锁定。

间隙可能跨越单个索引值,多个索引值,甚至可能为空。

间隙锁是性能和并发之间权衡的一部分,用于某些事务隔离级别而不是其他级别。

使用唯一索引锁定行以搜索唯一行的语句不需要间隙锁定。(这不包括搜索条件仅包含多列唯一索引的某些列的情况;在这种情况下,确实会发生间隙锁定。)例如,如果id列具有唯一索引,则以下语句仅使用具有id值100 的行的索引记录锁定,其他会话是否在前一个间隙中插入行无关紧要:

SELECT * FROM child WHERE id = 100;

如果id未编入索引或具有非唯一索引,则该语句会锁定前一个间隙。

此处值得注意的是,冲突锁可以通过不同的事务保持在间隙上。例如,事务A可以在间隙上保持共享间隙锁定(间隙S锁定),而事务B在同一间隙上保持独占间隙锁定(间隙X锁定)。允许冲突间隙锁定的原因是,如果从索引中清除记录,则必须合并由不同事务保留在记录上的间隙锁定。

间隙锁定InnoDB是“ 纯粹抑制 ”,这意味着它们的唯一目的是防止其他事务插入间隙。间隙锁可以共存。一个事务占用的间隙锁定不会阻止另一个事务在同一个间隙上进行间隙锁定。共享和独占间隙锁之间没有区别。它们彼此不冲突,它们执行相同的功能。

可以明确禁用间隙锁定。如果将事务隔离级别更改为,则会发生这种情况 READ COMMITTED。在这些情况下,对于搜索和索引扫描禁用间隙锁定,并且仅用于外键约束检查和重复键检查。

使用READ COMMITTED隔离级别还有其他影响 。在MySQL评估WHERE条件后,将释放非匹配行的记录锁。对于 UPDATE语句,InnoDB 执行“ 半一致 ”读取,以便将最新提交的版本返回给MySQL,以便MySQL可以确定该行是否符合WHERE 条件UPDATE。

  • Next-Key Locks

下一键锁定是索引记录上的记录锁定和索引记录之前的间隙上的间隙锁定的组合。(个人理解为区间的锁)

InnoDB以这样的方式执行行级锁定:当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享锁或排它锁。因此,行级锁实际上是索引记录锁。索引记录上的下一键锁定也会影响该索引记录之前的“ 间隙 ”。也就是说,下一键锁定是索引记录锁定加上索引记录之前的间隙上的间隙锁定。如果一个会话R在索引中具有共享或独占锁定记录 ,则另一个会话不能R在索引顺序之前的间隙中插入新索引记录 。

假设索引包含值10,11,13和20.此索引的可能的下一个键锁定覆盖以下间隔,其中圆括号表示排除间隔端点,方括号表示包含端点:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

对于最后一个间隔,下一个键锁定将间隙锁定在索引中最大值之上,而“ supremum ” 伪记录的值高于索引中实际的任何值。supremum不是真正的索引记录,因此,实际上,此下一键锁定仅锁定最大索引值之后的间隙。

默认情况下,InnoDB以 REPEATABLE READ事务隔离级别运行。在这种情况下,InnoDB使用下一键锁进行搜索和索引扫描,这会阻止幻像行(请参见第15.7.4节“幻影行”)。

下一键锁的事务数据SHOW ENGINE INNODB STATUS与 InnoDB监视器 输出中的以下内容类似:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` 
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;
  • 意向插入锁(Insert Intention Locks)

插入意图锁定是一种由INSERT行插入之前的操作设置的间隙锁定 。该锁定表示以这样的方式插入的意图:如果插入到相同索引间隙中的多个事务不插入间隙内的相同位置,则不需要等待彼此。假设存在值为4和7的索引记录。分别尝试插入值5和6的单独事务,在获取插入行上的排它锁之前,每个锁定4和7之间的间隙和插入意图锁,但是不要互相阻塞因为行是非冲突的。

以下示例演示了在获取插入记录的独占锁之前采用插入意图锁定的事务。该示例涉及两个客户端,A和B.

客户端A创建一个包含两个索引记录(90和102)的表,然后启动一个事务,该事务对ID大于100的索引记录放置独占锁。独占锁包括记录102之前的间隙锁:

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

客户端B开始事务以将记录插入间隙。该事务在等待获取独占锁时采用插入意图锁。

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

插入意图锁的事务数据SHOW ENGINE INNODB STATUS与 InnoDB监视器 输出中的以下内容类似 :

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...
  • 自增锁(AUTO-INC Locks)

一个AUTO-INC锁是通过交易将与表中取得一个特殊的表级锁 AUTO_INCREMENT列。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务必须等待对该表执行自己的插入,以便第一个事务插入的行接收连续的主键值。

该innodb_autoinc_lock_mode 配置选项控制用于自动增加锁定的算法。它允许您选择如何在可预测的自动增量值序列和插入操作的最大并发之间进行权衡。

posted @ 2019-08-29 17:08  q兽兽  阅读(238)  评论(0编辑  收藏  举报