MySQL锁--03---意向锁(Intention Locks)间隙锁(Gap Locks)临键锁(Next-Key Locks)
Posted 高高for 循环
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL锁--03---意向锁(Intention Locks)间隙锁(Gap Locks)临键锁(Next-Key Locks)相关的知识,希望对你有一定的参考价值。
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
意向锁(Intention Locks)
需要强调一下,意向锁是一种不与行级锁冲突表级锁,这一点非常重要。
意向锁分为两种:
1)意向共享锁(IS锁):事务在请求S锁前,要先获得IS锁
2)意向排他锁(IX锁):事务在请求X锁前,要先获得IX锁
– 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
SELECT column FROM table ... LOCK IN SHARE MODE;
– 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
-- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
SELECT column FROM table ... FOR UPDATE;
意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
为啥要有意向锁呢
innodb下有行锁,也有表级锁。
- 如果事务A要给一张表加表写锁,肯定是不允许这张表有其他事务B已经在某行数据上加了行写锁的。那么mysql怎么去检索一张表里面的数据有没有行写锁啊,一条一条去看,是不是效率也太低了~
- 所以这里衍生了意向锁。如果事务B对某行数据加了行写锁(属于排他锁哦),那么B也会同样给这张表加一个意向排他锁,等到事务A来加表锁的时候,事务A
会看到这张表上有了意向排它锁,事务A就不再进行加锁操作了。
意向锁的兼容互斥性
1. 意向锁之间是互相兼容的
2. 意向锁不会与行级的共享 / 排他锁互斥!!!
3. 意向锁与普通的表级排他 / 共享锁互斥:
总结
- InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。
- 意向锁之间互不排斥,但除了 IS 与 S 兼容外,意向锁会与 共享锁 / 排他锁 互斥。
- IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。
- 意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。
间隙锁(Gap Locks)
MySQL InnoDB支持三种行锁定方式:
- 行锁(Record Lock):
锁直接加在索引记录上面。 - 间隙锁(Gap Lock):
锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。 - 临键锁 ( Next-Key Lock )
行锁与间隙锁组合起来用就叫做Next-Key Lock。
快照读 和 当前读
InnoDB的默认隔离级别RR(可重复读),在RR下读数据有两种方式:
1. 快照读:-mvcc
普通的 select… 查询都是快照读
mvcc — 基于版本的控制协议
- 在MVCC下,事物开启执行第一个SELECT语句后会获取一个数据快照,直到事物结束读取到的数据都是一致的
2. 当前读: -next-key lock
读取的数据的最新版本,并且在读的时候不允许其它事物修改当前记录
临键锁(Next-Key Locks),也就是结合gap锁与行锁,
实现:
-
快照读(snapshot read)
简单的select操作(不包括 select … lock in share mode, select … for update) -
当前读(current read)
select … lock in share mode、select … for update
insert、update、delete
间隙锁概念:
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,
- nnoDB会给符合条件的已有数据记录的索引项加锁;
- 对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁.
举例 1
假如user表中只有101条记录,其empid的值分别是 1,2,…,100,101,
select * from user where user_id > 100 for update;
- 是一个范围条件的检索,InnoDB不仅会对符合条件的user_id值为101的记录加锁,
- 也会对user_id大于101(这些记录并不存在)的“间隙”加锁。
举例 2
这是在可重复读前提下,更有利于解决幻读的问题。如图:0,5,10,15,20,25这些数据之间的间隔 就是他们的间隙,而间隙锁就是 对他们这一部分空间进行加锁。
- 而行锁就是对存在数据的锁,比如给5这条数据加锁,也就是锁住了5这一行数据。而行锁和间隙锁组合起来也就是 next-key lock.上面的数据 也就形成了7个间隙锁。
- (-∞,0]、(0,5]、(5,10]、(10,15]、(15,20]、(20, 25]、(25, +supremum]。
InnoDB 使用间隙锁的目的
防止幻读
- 上述案例,要是不使用间隙锁,如果其他事务插入了user_id大于100的任何记录,那么本事务如果再次执行select * from user where user_id > 100 for update;,就会发生幻读
RR隔离级别
当前读: 依靠next-key来解决幻读问题,
快照读: 使用mvcc解决幻读
案例1 :
建表innodb_lock:
DROP TABLE IF EXISTS `innodb_lock`;
CREATE TABLE `innodb_lock` (
`a` int(10) NOT NULL,
`b` varchar(255) NOT NULL DEFAULT '',
KEY `index_a` (`a`),
KEY `index_b` (`b`)
) ENGINE=InnoDB;
插入数据,注意这里边没有a为2的数据:
INSERT INTO `innodb_lock` VALUES ('1', 'b2');
INSERT INTO `innodb_lock` VALUES ('3', '3');
INSERT INTO `innodb_lock` VALUES ('4', '4000');
INSERT INTO `innodb_lock` VALUES ('5', '5000');
INSERT INTO `innodb_lock` VALUES ('6', '6000');
INSERT INTO `innodb_lock` VALUES ('7', '7000');
INSERT INTO `innodb_lock` VALUES ('8', '8000');
INSERT INTO `innodb_lock` VALUES ('9', '9000');
(1)开启两个客户端,修改事务隔离级别为可重复读
(2)开启事务,在左侧客户端批量修改a为1~6范围内的数据。在右侧客户端插入a为2的数据。右侧操作被阻塞。说明有间隙锁。
(3)重复(2),事务隔离级别依然是repeatable read,只不过变成在右侧客户端插入a为10的数据,成功。
(4)事务隔离级别设置为read committed,重复步骤(2),发现右侧客户端的操作成功。说明该隔离级别无间隙锁。
(5)还要特别说明的是,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!左侧客户端给不存在的记录加锁,右侧客户端的增加操作阻塞。
但是,如果a是唯一索引,不会升级全表锁。先添加唯一索引:
(6)重复步骤(5),发现右侧客户端不会被阻塞,数据插入成功
临键锁(Next-Key Locks)
行锁与间隙锁组合起来用就叫做Next-Key Lock。
加锁规则(RR隔离级别下)
1.原则
- 原则1:加锁的基本单位是next-key lock。(是一个前开后闭的区间)
- 原则2:查找过程中访问到的对象才加锁
2.优化
-
优化1:唯一索引上的等值查询,匹配上时,给唯一索引加锁,next-key-lock会退化为行锁。
-
优化2:索引上的等值查询,向右遍历且最后一个值不满足等值条件的时候,next-key-lock退化为 间隙锁 Gap Locks 。
-
优化3:唯一索引和普通索引在范围查询的时候 都会访问到不满足条件的第一个值为止
案例 2 :
在上面的数据表我们可以得到5个next-key lock 区间:
-
唯一索引(id):(-∞,1],(1,4],(4,6],(6,9] ,(9,+supremum]
-
非唯一索引(age):(-∞,18],(18,21],(21,23],(23,26] ,(26,+supremum]
1.唯一索引等值查询:
当索引项存在时: next-key-lock会退化为行锁
当索引项不存在时: 向右遍历且最后一个值不满足等值条件的时候,next-key-lock退化为 间隙锁 Gap Locks。
2. 唯一索引范围查询:
3. 非唯一索引等值查询:
4 .非唯一索引范围查询:
以上是关于MySQL锁--03---意向锁(Intention Locks)间隙锁(Gap Locks)临键锁(Next-Key Locks)的主要内容,如果未能解决你的问题,请参考以下文章
Mysql 死锁过程及案例详解之插入意向锁与自增锁备份锁日志锁Insert Intention Lock Auto-increment Lock Backup Lock Log Lock