InnoDB 的锁
2024-05-10 11:03 abce 阅读(27) 评论(0) 编辑 收藏 举报共享锁和排它锁
InnoDB 实现的标准的行级锁有两种类型:共享锁(S)和排它锁(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 ... FOR SHARE 加的是 IS 锁,而 SELECT ... FOR UPDATE 加的是 IX 锁。
意向锁的锁定协议如下:
·事务在获取表中某一行的共享锁之前,必须先获取表的 IS 锁或更强的锁。
·事务在获取表中某一行的排他锁之前,必须先获取表的 IX 锁。
表级锁兼容性总结如下。
X |
IX |
S |
IS |
|
X |
冲突 |
冲突 |
冲突 |
冲突 |
IX |
冲突 |
兼容 |
冲突 |
兼容 |
S |
冲突 |
冲突 |
兼容 |
兼容 |
IS |
冲突 |
兼容 |
兼容 |
兼容 |
除了对整个表请求(例如,LOCK TABLES ... WRITE)之外,意向锁不会阻塞任何操作。意向锁的主要目的是显示有人正在锁定或将要锁定表中的某一行。
意向锁的事务数据在 SHOW ENGINE INNODB STATUS 和 InnoDB 监控输出中类似于下面的内容:
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 可以在一个间隙上持有共享间隙锁,而事务 B 则在同一间隙上持有独占间隙锁。允许冲突间隙锁的原因是,如果从索引中清除记录,不同事务在该记录上持有的间隙锁必须合并。
InnoDB 中的间隙锁是 "纯抑制性 "的,这意味着它们的唯一目的是防止其他事务插入到间隙中。间隙锁可以共存。一个事务获取的间隙锁不会阻止另一个事务在同一间隙获取间隙锁。共享间隙锁和独占间隙锁没有区别。它们互不冲突,执行相同的功能。
间隙锁可以显式禁用。如果将事务隔离级别改为 READ COMMITTED,就会出现这种情况。在这种情况下,间隙锁定会禁用于搜索和索引扫描,仅用于外键约束检查和重复键检查。
使用 READ COMMITTED 隔离级别还有其他影响。在 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)
对于最后一个区间间隔,下一键锁锁定的是索引中最大值和 "最高值"伪记录上的间隙,"最高值"伪记录的值高于索引中的任何实际值。最高值不是真正的索引记录,因此实际上,下一键锁定只锁定最大索引值之后的间隙。
默认情况下,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 之间的间隙,但不会相互阻塞,因为这些行是不冲突的。
下面的示例演示了事务在获得插入记录的排它锁之前使用插入意向锁的情况。该示例涉及两个客户端 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-INC 锁是一种特殊的表级锁,由插入带有 AUTO_INCREMENT 列的表的事务使用。在最简单的情况下,如果一个事务正在向表中插入值,那么其他任何事务都必须等待,这样第一个事务插入的行就会获得连续的主键值。
innodb_autoinc_lock_mode 变量控制自增锁的算法。通过它,你可以选择如何在可预测的自动递增值序列和插入操作的最大并发量之间进行权衡。
空间索引的谓词锁
InnoDB 支持对包含空间数据的列进行 SPATIAL 索引。
为了处理涉及 SPATIAL 索引的操作的锁定,下一键锁定不能很好地支持 REPEATABLE READ 或 SERIALIZABLE 事务隔离级别。多维数据中没有绝对排序概念,因此不清楚哪个是 "下一个"键。
为了支持带有 SPATIAL 索引的表的隔离级别,InnoDB 使用了谓词锁。SPATIAL 索引包含最小边界矩形(MBR)值,因此 InnoDB 会对查询使用的 MBR 值设置谓词锁,从而实现对索引的一致读取。其他事务无法插入或修改符合查询条件的记录。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2022-05-10 使用v$wait_chains分析系统为何hang住
2022-05-10 docker中postgresql的备份和还原