mysql innodb锁

本文详细介绍了MySQL InnoDB存储引擎中的各种锁类型及其工作原理,包括共享锁、排他锁、意向锁、记录锁、间隙锁、临键锁、插入意向锁、自增锁以及用于空间索引上的锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍InnoDB使用的锁类型

  • 共享和独占锁
  • 意向锁
  • 记录锁
  • 间隙锁
  • 临建锁
  • 插入意向锁
  • 自增锁
  • 用于空间索引上的锁

共享和独占锁

InnoDB实现了标准的行级锁,其中有两种锁,共享(S)锁和排他(X)锁。

  • 共享锁允许持有该锁的事务读取一行。
  • 排他(X)锁允许持有该锁的事务更新或删除一行。

如果事务T1持有第r行上的共享(S)锁,那么不同的事务T2对第r行上的锁的请求将按如下方式处理:

  • 通过T2对S锁的请求可以立即被授予。结果,T1和T2都对r持有S锁。
  • T2对X锁的请求不能立即被授予。

如果事务T1在第r行上持有排他(X)锁,那么来自某个不同事务T2的对r上任一类型的锁的请求就不能立即授予。相反,事务T2必须等待事务T1释放它在第r行上的锁

意向锁

InnoDB支持多粒度锁,允许行锁和表锁共存。例如,LOCK TABLES…WRITE接受指定表上的排他锁(X锁)。为了实现多粒度级别的锁定,InnoDB使用了意向锁。意向锁是表级锁,它指示事务以后需要对表中的某一行使用哪种类型的锁(共享的或独占的)。有两种类型的意向锁:

  • 意图共享锁(IS)表示事务打算在表中的单个行上设置共享锁。
  • 意图排他锁(IX)表示事务打算在表中的各个行上设置排他锁。
    例如,SELECT…LOCK IN SHARE MODE设置一个IS锁,并且SELECT…FOR UPDATE设置IX锁。
    意向锁定协议如下:
  • 事务在获得表中某一行上的共享锁之前,必须首先获得表上的IS锁或更强的锁。
  • 事务在获得表中某行上的排他锁之前,必须首先获得表上的IX锁。
    表级锁类型兼容性总结在下面的表中。
    在这里插入图片描述
    如果锁与现有锁兼容,则将锁授予请求事务,但如果与现有锁冲突则不授予。事务等待,直到冲突的现有锁被释放。如果一个锁请求与一个现有的锁冲突,并且因为它会导致死锁而不能被授予,则会发生错误。
    意向锁不阻塞任何东西,除了全表请求(例如,LOCK TABLES…写)。意向锁的主要目的是显示某人正在锁定一行,或将锁定表中的一行。
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

记录锁

记录锁是索引记录上的锁。例如,SELECT c1 FROM t WHERE c1 = 10 For UPDATE;阻止任何其他事务插入、更新或删除t.c1值为10的行。
记录锁总是锁定索引记录,即使表定义时没有索引。在这种情况下,InnoDB会创建一个隐藏的聚集索引,并使用该索引进行记录锁定。
记录锁的事务数据在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        ;;

间隙锁

间隙锁是对索引记录之间的间隙的锁,或者对第一个索引记录之前或最后一个索引记录之后的间隙的锁。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 For UPDATE;阻止其他事务在列t.c1中插入值15,无论该列中是否已经有这样的值,因为范围中所有现有值之间的间隔都是锁定的。
间隙可能跨越单个索引值、多个索引值,甚至为空。

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

对于使用惟一索引锁定行以搜索惟一行的语句,不需要间隙锁定。(这不包括搜索条件只包括多列唯一索引的某些列的情况;在这种情况下,间隙锁定就会发生。)例如,如果id列有唯一的索引,下面的语句只对id值为100的行使用索引记录锁,而不管其他会话是否在前面的间隙中插入行:

SELECT * FROM child WHERE id = 100;

如果id没有索引或具有非惟一的索引,则该语句将锁定前面的间隙。

这里还值得注意的是,冲突锁可以由不同的事务持有在一个间隙上。例如,事务A可以持有一个间隙上的共享间隙锁(间隙s锁),而事务B可以持有同一个间隙上的独占间隙锁(间隙x锁)。允许间隙锁冲突的原因是,如果从索引中清除一条记录,则必须合并不同事务在该记录上持有的间隙锁。

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

间隙锁定可以显式禁用。如果将事务隔离级别更改为READ COMMITTED,或者启用innodb_locks_unsafe_for_binlog系统变量(现在已弃用),就会发生这种情况。在这种情况下,间隙锁定对搜索和索引扫描禁用,只用于外键约束检查和重复键检查。

使用READ COMMITTED隔离级别或启用innodb_locks_unsafe_for_binlog还有其他影响。MySQL在WHERE条件求值后释放不匹配行的记录锁。对于UPDATE语句,InnoDB进行“半一致”读取,这样它就会返回最新提交的版本给MySQL,这样MySQL就可以判断该行是否符合UPDATE的WHERE条件。

临键锁

临键锁是索引记录上的记录锁和索引记录前间隙上的间隙锁的组合。

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

假设索引包含值10、11、13和20。该索引可能的临键锁覆盖以下间隔,其中圆括号表示不包含间隔端点,方括号表示包含该端点:

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

对于最后一个间隔,临键锁锁定索引中最大值和值高于索引中实际值的“supremum”伪记录之间的间隔。上值并不是一个真正的索引记录,因此,实际上,这个下一个键锁只锁定最大索引值之后的间隙。

默认情况下,InnoDB运行在REPEATABLE READ事务隔离级别。在这种情况下,InnoDB使用临键锁进行搜索和索引扫描,这就防止了幻读。

临键锁的事务数据在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操作在插入行之前设置。该锁表示要以这样一种方式插入,即插入到同一索引间隙的多个事务如果不在间隙内的同一位置插入,则不需要相互等待。假设有值为4和7的索引记录。分别尝试插入值5和6的独立事务,每个事务在获得插入行上的排他锁之前,都会用插入意图锁锁定4和7之间的间隙,但不会相互阻塞,因为插入的行上的值为5和6。

下面的例子演示了一个事务在获取插入记录的排他锁之前先获取一个插入意图锁。该示例涉及两个客户机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锁是一种特殊的表级锁,由插入具有AUTO_INCREMENT列的表的事务获得。在最简单的情况下,如果一个事务正在向表中插入值,那么任何其他事务都必须等待自己对该表进行插入,以便第一个事务插入的行接收连续的主键值。

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

用于空间索引上的锁

InnoDB支持用于空间索引上的锁

要处理涉及空间索引的操作的锁定,临键锁不能很好地支持REPEATABLE READ或SERIALIZABLE事务隔离级别。在多维数据中没有绝对的排序概念,因此不清楚哪个是“下一个”键。

为了启用对具有SPATIAL索引的表的隔离级别支持,InnoDB使用了谓词锁。SPATIAL索引包含最小边界矩形(MBR)值,因此InnoDB通过在用于查询的MBR值上设置谓词锁来强制索引上的一致性读取。其他事务不能插入或修改与查询条件匹配的行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值