首页/应用软件/内容

MySQL数据库InnoDB引擎行级锁锁定范围详细说明

应用软件2022-09-15 阅读()
+----+-------------+-------+-------+---------------+------------+---------+------+------+-----------------------+ 1 row in set (0.00 sec)

是不是和第一次执行结果不同了,rows参数等于2,说明扫描了两行记录,结合SQL语句select * from user where name>'e' for update;执行后返回结果我们判断这两行记录应该为g和i。

因为select * from user where name>'e' for update;语句扫描了两行索引记录分别是g和i,所以我们将g和i的锁定范围叠就可以得到where name>'e'的锁定范围:

  1. 索引记录g在name列锁定范围为(e,g],(g,i)。索引记录i的在name列锁定范围为(g,i],(i,+∞)。两者叠加后锁定范围为(e,g],(g,i],(i,+∞)。其中g,i为索引记录锁。

  2. g和i对应id列中的7和9加索引记录锁。

  3. name列的值为锁定范围上边界e时,还会在e所对应的id列值为5之后的所有值之间加上间隙锁,范围为(5,7),(7,9),(9,+∞)。下边界为+∞无需考虑。

接下来我们逐一测试:

首先测试验证了next-key锁范围,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`, `age`) VALUES ('10', #{name},'18');
5rollback;--
6--rollback;

替换步骤5中name的值,观察结果:

name的值执行结果
a不阻塞
b不阻塞
c不阻塞
d不阻塞
f阻塞
g阻塞
h阻塞
i阻塞
j阻塞
k阻塞

下面验证next-key锁中哪部分是间隙锁,哪部分是索引记录锁,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where name=#{name} for update;
5rollback;--
6--rollback;

替换步骤5中name的值,观察结果:

name的值执行结果
e不阻塞
f不阻塞
g阻塞
h不阻塞
i阻塞
j不阻塞

接下来验证对id列加索引记录锁,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where id=#{id} for update;
5rollback;--
6--rollback;

替换步骤5中id的值,观察结果:

id的值执行结果
5不阻塞
6 不阻塞
7阻塞
8不阻塞
9阻塞
10不阻塞

最后我们验证name列的值为边界数据e时,id列间隙锁的范围,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`,`age`) VALUES (#{id}, 'e','18');
5rollback;--
6--rollback;

替换步骤5中id的值,观察结果:

id的值执行结果
-1不阻塞
1不阻塞
2不阻塞
3不阻塞
4不阻塞
5不阻塞
6阻塞
7阻塞
8阻塞
9阻塞
10阻塞
11阻塞
12阻塞

注意7和9是索引记录锁记录锁

观察上面的所有SQL语句执行结果,可以验证select * from user where name>'e' for update的锁定范围为此语句扫描name列索引记录g和i的锁定范围的叠加组合。

2.4 场景四

我们通过场景三验证了普通索引的范围查询语句加锁范围,现在我们来验证一下唯一索引的范围查询情况下的加锁范围。有了场景三的铺垫我们直接跳过扫描全部索引的情况,创建可以扫描范围记录的表结构并插入相应数据测试。

建表:

CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `name` varchar(8) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入数据:

INSERT INTO `user` (`id`, `name`,`age`) VALUES ('1', 'a','15');
INSERT INTO `user` (`id`, `name`,`age`) VALUES ('3', 'c','20');
INSERT INTO `user` (`id`, `name`,`age`) VALUES ('5', 'e','16');
INSERT INTO `user` (`id`, `name`,`age`) VALUES ('7', 'g','19');
INSERT INTO `user` (`id`, `name`,`age`) VALUES ('9', 'i','34');

和场景三表唯一不同是name列为唯一索引。

SQL语句select * from user where name>'e'扫描name列两条索引记录g和i。如果需要只对g和i这两条记录加上记录锁无法避免幻读的发生,索引锁定范围应该还是两条数据next-key锁锁的组合:(e,g],(g,i],(i,+∞)。其中g,i为索引记录锁

我们通过SQL验证我们的结论,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`, `age`) VALUES ('10', #{name},'18');
5rollback;--
6--rollback;

替换步骤5中name的值,观察结果:

name的值执行结果
a不阻塞
b不阻塞
c不阻塞
d不阻塞
f阻塞
g阻塞
h阻塞
i阻塞
j阻塞
k阻塞

下面验证next-key锁中哪部分是间隙锁,哪部分是索引记录锁,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where name=#{name} for update;
5rollback;--
6--rollback;

替换步骤5中name的值,观察结果:

name的值执行结果
e不阻塞
f不阻塞
g阻塞
h不阻塞
i阻塞
j不阻塞

通过上面两条SQL语句的验证结果,我们证明了我们的g和i的锁定范围趋势为两者next-key叠加组合。

接下来我们验证一下对辅助索引加锁后对聚合索引的锁转移,执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--SELECT * FROM user where id=#{id} for update;
5rollback;--
6--rollback;

替换步骤5中id的值,观察结果:

id的值执行结果
5不阻塞
6 不阻塞
7阻塞
8不阻塞
9阻塞
10不阻塞

由结果可知对辅助索引name中的g和i列对应的聚合索引id列中的7和9加上了索引记录锁。

到目前为止所有实验结果和场景三完全一样,这也很好理解,毕竟场景四和场景三只是辅助索引name的索引类型不同,一个是唯一索引,一个是普通索引。

最后验证意向,next-key锁边界数据e,看看结论时候和场景三相同。

执行SQL语句的模板:

步骤client 1client 2
1begin;--
2SELECT * FROM user where name>'e' for update;--
3--begin;
4--INSERT INTO `user` (`id`, `name`,`age`) VALUES (#{id}, 'e','18');
5rollback;--
6--rollback;

替换步骤5中id的值,观察结果:

id的值执行结果
-1不阻塞
1不阻塞
2不阻塞
3不阻塞
4不阻塞
5不阻塞
6不阻塞
7阻塞
8不阻塞
9阻塞
10不阻塞
11不阻塞
12不阻塞

注意7和9是索引记录锁记录锁

通过结果可知,当name列为索引记录上边界e时,并没有对id有加锁行为,这点与场景三不同。

对于唯一索引的范围查询和普通索引的范围查询类似,唯一不同的是当辅助索引等于上下范围的边界值是不会对主键加上间隙锁。

唯一索引范围查询加锁范围:

结论

InnoDB引擎会对他扫描过的索引记录加上相应的锁,通过“场景一”我们已经明确了扫描一条普通索引记录的锁定范围,通过“场景三”我们可以推断任意多个扫描普通索引索引记录的锁定范围。通过“场景二”我们确定了扫描一条唯一索引记录(或主键)的锁定范围。通过“场景四”我们可以推断任意多个扫描索唯一引记录(或主键)的锁定范围。在实际的应用可以灵活使用,判断两条SQL语句是否相互锁定。这里还需要注意的是对于索引的查询条件,不能想当然的理解,他往往不是我们理解的样子,需要结合执行计划判断索引最终扫描的记录数,否则会对加锁范围理解产生偏差。




备注

注1:在事务隔离级别为SERIALIZABLE时,普通的select语句也会对语句执行过程中扫描过的索引加上next-key锁。如果语句扫描的是唯一索引,那就将next-key锁降级为索引记录锁了。
注2:当更新语句修改聚合索引(主键)记录时,会对受影响的辅助索引执行隐性的加锁操作。当插入新的辅助索引记录之前执行重复检查扫描时和当插入新的辅助索引记录时,更新操作还对受影响的辅助索引记录添加共享锁。

相关推荐:

mysql执行sql文件报错Error: Unknown storage engine‘InnoDB如何解决

MySQL启动时InnoDB引擎被禁用了怎么办

MySQL存储引擎MyISAM和InnoDB之间的比较

以上就是MySQL数据库InnoDB引擎行级锁锁定范围详解的详细内容,更多请关注php中文网其它相关文章!


学习教程快速掌握从入门到精通的SQL知识。



第1页  第2页  第3页  第4页  第5页  第6页  第7页  第8页  第9页  第10页  第11页  第12页  第13页  第14页  第15页  第16页  第17页  第18页  第19页  第20页  第21页  第22页  第23页  第24页  第25页  第26页  第27页  第28页  第29页  第30页  第31页  第32页  第33页  第34页  第35页  第36页  第37页  第38页  第39页  第40页  第41页  第42页  第43页  第44页  第45页 

……

相关阅读