意向锁:
- 意向共享锁(读锁 IS ),事务想要获取一张表的几行数据的共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁 。
- 意向排他锁(写锁 IX),事务想要获取一张表中几行数据的排它锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁 。
- 解释一下意向锁
锁的算法
InnoDB有三种行锁的算法:
- Record Lock:单个行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,而非记录本身
- Next-Key Lock:结合Gap Lock和Record Lock,锁定一个范围,并且锁定记录本身 。主要解决的问题是REPEATABLE READ隔离级别下的幻读 。可以参考文章了解事务隔离级别的相关知识点 。
注意了,如果走唯一索引,那么Next-Key Lock会降级为Record Lock,即仅锁住索引本身,而不是范围 。也就是说Next-Key Lock前置条件为事务隔离级别为RR且查询的索引走的非唯一索引、主键索引 。
下面我们用个例子详细说一下 。
首先建立一张表:
CREATE TABLE T (id int ,f_id int,PRIMARY KEY (id), KEY(f_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8insert into T SELECT 1,1;insert into T SELECT 3,1;insert into T SELECT 5,3;insert into T SELECT 7,6;insert into T SELECT 10,8;事务A执行如下语句:
SELECT * FROM T WHERE f_id = 3 FOR UPDATE这时SQL语句走非唯一索引,因此使用Next-Key Locking加锁,并且有2个索引,其需要分别进行锁定 。
对于聚集索引,其仅对id等于5的索引加上Record Lock 。而对于辅助索引,其加上Next-Key Lock,锁定了范围(1,3),特别需要注意的是,InnoDB存储引擎还会对辅助索引下一个键值加上Gap Lock,即范围(3.6)的锁 。
所以如果在新session中执行如下语句都会报错[Err] 1205 - Lock wait timeout exceeded; try restarting transaction:
select * from T where id = 5 lock in share MODE -- 不能执行,因为事务A已经给id=5的值加上了X锁,执行会被阻塞INSERT INTO T SELECT 4,2 -- 不能执行,辅助索引的值为2,在(1,3)的范围内,执行阻塞INSERT INTO T SELECT 6,5 -- 不能执行,gap锁会锁住(3,6)的范围,执行阻塞此时想象一下,事务A锁定了f_id =5 的记录, 正常会有个gap lock,锁住(5,6),那么如果没有(5,6)的gap锁,那么用户可以插入索引 f_id 为5的记录,这样事务A再次查询就会返回一个不同的记录,也就导致了幻读的产生 。
同理,如果我们事务A执行的是select * from T where f_id = 10 FOR UPDATE,在表里查不到数据,但是基于Next-Key Lock会锁住(8,+∞),我们执行INSERT INTO T SELECT 6,11是无法插入成功的,这就从根本上解决了幻读问题 。
推荐阅读
- docker运行elasticsearch
- 思科交换机,路由器如何关闭telnet 开启ssh服务
- 淘宝店退了后重新再开可以吗 淘宝如何退店重开
- 社保交满15年了,如何办理退休?很多人不知道,收藏有用
- 分析MySQL应用架构发展演变史
- 如何让黑裤子重新染黑 黑牛仔裤发白怎么处理恢复
- 立冬节气如何养生保健?注意藏精保暖
- 雨水节气如何养生?专家:调养脾胃是关键
- 冬季如何养生?大雪节气养生注意这四点
- 女性冬季如何补血?中医专家:“吃好睡好”足够
