InnoDB锁模型介绍

InnoDB锁模型


InnoDB锁粒度可到达 ,行锁基于索引。

锁的类型

共享锁(读锁):S

排他锁(写锁):X

不同类型锁的共存性

XS
X✖️✖️
S✖️✔️

锁的分类

意向锁(Intention Locks)

表级锁,分为意向共享锁(IS)意向排他锁(IX),可以理解为一种声明,即“我将要或正在为该表的某些行加锁”。所有类型的意向锁之间可共存,因为打算加锁的行可能不一样。

表锁的共存性

XIXSIS
X✖️✖️✖️✖️
IX✖️✔️✖️✔️
S✖️✖️✔️✔️
IS✖️✔️✔️✔️

查看意向锁

SHOW ENGINE INNODB STATUS;
-- InnoDB monitor 中会有如下锁类型说明
TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

记录锁(Record Locks)

顾名思义,记录锁就是为某行记录加锁,基于主键索引(Primary)或唯一索引(Unique),它封锁该行的索引记录:

  • - id 列为主键列或唯一索引列
  • SELECT * FROM table WHERE id = 1 FOR UPDATE;

id 为 1 的记录行会被锁住。需要注意的是:id 列必须为唯一索引列或主键列,否则上述语句加的锁就会变成临键锁。同时查询语句必须为精准匹配(=),不能为 >、<、like等,否则也会退化成临键锁。

在通过 主键索引 与 唯一索引 对数据行进行 UPDATE 操作时,也会对该行数据加记录锁:

  • - id 列为主键列或唯一索引列
  • UPDATE SET age = 50 WHERE id = 1;

查看记录锁

SHOW ENGINE INNODB STATUS;
-- InnoDB monitor 中会有如下锁类型说明

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)

间隙锁基于非唯一索引(单列index或多列unique索引的部分列**)**,它锁定一段范围内的索引记录。请务必牢记:使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;

即所有在(1,10)区间内的记录行都会被锁住,所有id 为 2、3、4、5、6、7、8、9 的数据行的插入会被阻塞,但是 1 和 10 两条记录行并不会被锁住。除了手动加锁外,在执行完某些 SQL 后,InnoDB 也会自动加间隙锁,这个我们在下面会提到。

间隙锁可以覆盖单索引值、多列索引值甚至空值。

间隙锁用于隔离级别:可重复读。若为提交读级别,则间隙锁仅用于外键约束和重复键检查。

所有类型的间隙锁可共存,当部分行被真删除后,间隙锁将合并。

临键锁(Next-Key Locks)

临键锁 = 记录锁 + 间隙锁。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

举例

假设有如下表:MySql,InnoDB,Repeatable-Read:table(id PK, age KEY, name)

idagename
110Lee
324Soraka
532Zed
745Talon

该表中 age 列潜在的临键锁有:(-∞, 10],(10, 24],(24, 32],(32, 45],(45, +∞],在事务 A 中执行如下命令:

  • - 根据非唯一索引列 UPDATE 某条记录 UPDATE table SET name = Vladimir WHERE age = 24; -- 或根据非唯一索引列 锁住某条记录 SELECT * FROM table WHERE age = 24 FOR UPDATE;

不管执行了上述 SQL 中的哪一句,之后如果在事务 B 中执行以下命令,则该命令会被阻塞:

INSERT INTO table VALUES(100, 26, 'Ezreal');

很明显,事务 A 在对 age 为 24 的列进行 UPDATE 操作的同时,也获取了 (24, 32] 这个区间内的临键锁。

不仅如此,在执行以下 SQL 时,也会陷入阻塞等待:

INSERT INTO table VALUES(100, 30, 'Ezreal');

那最终我们就可以得知,在根据非唯一索引对记录行进行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操作时,InnoDB 会获取该记录行的临键锁 ,并同时获取该记录行下一个区间的间隙锁。即事务A在执行了上述的 SQL 后,最终被锁住的记录区间为 (10, 32)。

临键锁用于可重复读隔离级别,通过临建锁可以解决幻读的问题。

查看临键锁

SHOW ENGINE INNODB STATUS;
-- InnoDB monitor 中会有如下锁类型说明

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 ;;

插入意向锁

插入操作在插入行前需要获得的间隙锁,表示“想要在某个索引区间插入数据行”,如果插入的位置不同,插入意向锁间可以共存,但不能与普通间隙锁共存。

举例

会话1

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 |
+-----+

会话2

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

会话2会阻塞,执行SHOE ENGINE INNODB STATUS;

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

总结

①:InnoDB 中的行锁的实现依赖于索引,一旦某个加锁操作没有使用到索引,那么该锁就会退化为表锁。

②:记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。

③:间隙锁存在于非唯一索引中,锁定开区间范围内的一段间隔,它是基于临键锁实现的。

④:临键锁存在于非唯一索引中,该类型的每条记录的索引上都存在这种锁,它是一种特殊的间隙锁,锁定一段左开右闭的索引区间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaspardR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值