一、准备


创建 student

CREATE TABLE student (
id bigint NOT NULL,
age int DEFAULT NULL,
b int DEFAULT NULL,
a int DEFAULT NULL,
c int DEFAULT NULL,
PRIMARY KEY ( id ),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3



插⼊数据



insert into student (id,age,a,b,c) values(1,1,1,1,1),(5,5,5,5,5),(9,9,9,9,9),(11,11,11,11,11);

mysql临键锁加锁时机_mysql临键锁加锁时机

二、锁区间

mysql临键锁加锁时机_开发语言_02

1.什么是锁区间?

区间从字⾯意思解释就是⼀个范围。

2.什么范围呢?怎么建⽴的呢?

mysql的锁区间是在当前会话创建后事物开始时当前数据库已存在数据相邻数据之间两两作为区间边界值。

3.相邻数据⼜以什么作为依据建⽴锁区间呢,有以下情况?

1.查询条件是主键索引

就以主键索引从⼩到达两两数据以主键列作为数据依据建⽴锁区间

2.查询条件时普通索引

和主键索引⼀致

3.组合索引

以组合索引第⼀个字段为依据

4.两个读索引

a字段建⽴索引,b字段建⽴索引

a和b都作为查询条件,并且使⽤and作为条件,结果集求交集。

条件查询中涉及到的索引依照创建索引顺序排序,以排在第⼀个创建的索引作为锁区间依据字段。

5.组合索引

a字段建⽴索引,b字段建⽴索引

a和b都作为查询条件,并且使⽤or作为条件,结果集求并集。

  • 当or ⾛索引,合并索引,两个字段都作为锁区间依据字段,其他操作满⾜任何⼀个字段的锁区间都将阻塞。
  • 当or不⾛索引,全表查询,两个字段都作为锁区间依据字段,其他操作满⾜任何⼀个字段的锁区间都将阻 塞。

三、从锁区间看临键锁和间隙锁的区别



从区间上看,临键锁和间隙锁的区别是锁区间不同,临键锁包含右边界数据,间隙锁不包含边界数据。



四、记录锁(Record Locks)



select * from student where id=1 for update;



锁住 id=1 的记录。




mysql临键锁加锁时机_数据库_03


当前查询的id不存在时,将升级为间隙锁

疑问1:升级为间隙锁,锁的是什么锁区间?

测试查询id = 3,此时数据库不存在id为3的数据,

那么会以id字段作为依据找到id=3⼊的锁区间(1,5)

疑问2:为什么是间隙锁锁区间不是临键锁锁区间有什么依据?


如果是临键锁我们更改边界值会阻塞


建⽴会话 1 ,执⾏选中 s q l


--insert into student (id,age,a,b,c) values(2,2,2,2,2);
insert into student (id,age,a,b,c) values(4,4,4,4,4);
update student set age = 9 where id = 5;
insert into student (id,age,a,b,c) values(6,6,6,6,6);

mysql临键锁加锁时机_开发语言_04


建⽴会话 2 ,执⾏ s q l


mysql临键锁加锁时机_mysql临键锁加锁时机_05


进⼊阻塞状态

 

mysql临键锁加锁时机_java_06


执⾏会话 2 中的第⼆条 s q l


mysql临键锁加锁时机_mysql临键锁加锁时机_07


依然还是阻塞状态


 

mysql临键锁加锁时机_mysql临键锁加锁时机_08


执⾏会话 2 第三条 s q l ,修改边界值


 

mysql临键锁加锁时机_mysql临键锁加锁时机_09


并没有阻塞,执⾏成功



mysql临键锁加锁时机_数据库_10


数据修改成功年龄成功修改为 9



mysql临键锁加锁时机_java_11


我们可以测⼀下是否影响 id 在(5, 9 )区间的,执⾏选中 s q l


 

mysql临键锁加锁时机_数据库_12


执⾏成功


 

mysql临键锁加锁时机_java_13


因此当查询字段数值不存在时,只影响该数值落⼊的依据字段锁区间范围数据。


五、间隙锁(Gap Locks)


间隙锁的触发⽅式:当我们进⾏范围查询的时候会触发间隙锁


1.主键作为查询条件


思考


Select * from student where id >5 for update 锁的是哪⾥?


预测:


1. 落⼊(1, 5 )锁区间


2. 落⼊(5, 9 )锁区间


3. 落⼊(1, 5 )锁区间和(5, 9 )锁区间


4. 两个都不落⼊


会话1 执⾏



会话 2

 

mysql临键锁加锁时机_mysql临键锁加锁时机_14


可得并没有落⼊(1, 5 )锁区间


会话 2 ,执⾏插⼊ id=6 ,阻塞了


mysql临键锁加锁时机_字段_15


落⼊了(5, 9)锁区间

那好,我们修改⼀下会话 1 的 id 条件值, id>5 改成 id>7


mysql临键锁加锁时机_字段_16


会话 2 执⾏,看右上⻆,仍然是阻塞状态


 

mysql临键锁加锁时机_mysql临键锁加锁时机_17


因为 7 仍然是落⼊(5, 9 )锁区间,进⽽导致该锁区间有效。 那好我们接着测试(9, 11 ),( 11 ,正⽆穷⼤)



mysql临键锁加锁时机_数据库_18


mysql临键锁加锁时机_java_19


依然是阻塞状态,因为查询的条件是 >5 ,(9, 11 ),( 11 ,正⽆穷⼤)也属于该范围内的锁区间,进⽽也会⽣效。


2.普通索引作为查询条件


和主键⼀致


3.组合索引作为查询条件


表数据以及字段顺序如下



测试⽤例:


1. 和数据库字段保持⼀致


2. 改变查询字段顺序


3. 改变表结构字段顺序


4. 改变组合索引字段顺序


3.1数据库字段顺序保持⼀致


创建组合索引 a,b


mysql临键锁加锁时机_字段_20


会话 1 执⾏选中 s q l


 

mysql临键锁加锁时机_mysql临键锁加锁时机_21


会话 2 执⾏选中 s q l


 

mysql临键锁加锁时机_数据库_22


进⼊阻塞状态,因此 a >5 ⽣效,导致 a=6 时插⼊阻塞


我们将插⼊数据 a=6 改成 a=2 , b=11 改为 2 ,只让插⼊数据 b=2 落⼊ b<9 中,


 

mysql临键锁加锁时机_开发语言_23


此时并没有阻塞因此,⽣效的是 a 字段, b 字段虽然也是组合索引字段,但是并没有满⾜范围的锁区间⽣效。


思考:


因为 a 字段在查询条件之前的原因导致 a>5 的锁区间⽣效了吗?好的,带着疑问来下⾯。


3.2改变查询字段顺序


将 where a>5 and b<9 for update;


改为 :


where b <9 and a>5 for update;


执⾏会话 1 选中 s q l



执⾏会话 2


将 a=2 改为 6,b=2 改为 11

 

mysql临键锁加锁时机_字段_24


当 a=6 时依然阻塞 ,a>5 仍然⽣效


执⾏会话 2


将 a=6 改为 2,b=11 改为 2


mysql临键锁加锁时机_mysql临键锁加锁时机_25


当 a=2,b=2 时并没有阻塞,因此查询条件顺序并不会影响⽣效字段。


思考:


不是条件查询顺序影响,会不会是表结构顺序影响?好的,带着疑问继续来下⾯。


3.3 改变表结构字段顺序


b 字段放在 a 字段之前


mysql临键锁加锁时机_数据库_26


执⾏会话 1


mysql临键锁加锁时机_字段_27


执⾏会话 2


将 a=2 改为 6,b=2 改为 11

 

mysql临键锁加锁时机_mysql临键锁加锁时机_28


阻塞了。


执⾏会话 2


将 a=6 改为 2,b=11 改为 2

 

mysql临键锁加锁时机_java_29


执⾏成功。


此处做个⼩总结:


使⽤组合索引作为条件查询,⽣效的锁区间与表结构字段顺序、查询条件字段顺序⽆关。

3.4改变组合索引字段顺序


表结构恢复

 

mysql临键锁加锁时机_数据库_30


组合索引修改


mysql临键锁加锁时机_java_31


会话 1 执⾏选中 s q l

 

mysql临键锁加锁时机_mysql临键锁加锁时机_32


会话 2 执⾏选中 s q l

 

mysql临键锁加锁时机_mysql临键锁加锁时机_33


插⼊ a=6 ,没有阻塞,此时 a 字段没有⽣效


删除插⼊数据,将 a=6 改为 a=4 ,将 b=11 改为 b=8, 执⾏会话 2


mysql临键锁加锁时机_字段_34


修改数据后重新插⼊,阻塞了


总结: 仅有组合索引⾸字段作为锁区间依据。


4 两个索引作为查询条件


测试⽤例:


1. 按照表结构顺序和查询条件顺序⼀致


2. 改变查询条件顺序


3. 改变表结构字段顺序


前提:


1. 删除组合索引( a,b )


2. 建⽴普通索引 a,b


3. 删除表数据


4.1按照表结构顺序和查询条件顺序⼀致


会话 1 ,执⾏选中 s q l

 

mysql临键锁加锁时机_java_35


会话 2 ,执⾏选中 s q l

 

mysql临键锁加锁时机_java_36


当 a=6,b=11 时执⾏阻塞,因此 a>5 ⽣效。

会话 2 ,执⾏选中 s q l

将 a=6 改为 a=2,b=11 改为 b=8

 

mysql临键锁加锁时机_开发语言_37


并没有阻塞,


4.2改变查询条件顺序


会话 1 ,执⾏选中 s q l


mysql临键锁加锁时机_字段_38


会话 2 ,执⾏选中 s q l



阻塞了, a>5 ⽣效


会话 2 ,执⾏选中 s q l


mysql临键锁加锁时机_数据库_39


⽆阻塞, b<9 五⽣效


总结:两个索引的条件查询顺序不能影响使⽤哪个字段作为锁区间的依据


4.3改变表结构字段顺序


表结构


 

mysql临键锁加锁时机_开发语言_40


会话 1 ,执⾏选中 s q l


 

mysql临键锁加锁时机_java_41


会话 2 ,执⾏选中 s q l



mysql临键锁加锁时机_mysql临键锁加锁时机_42


a=6 阻塞了,因此 a>5 ⽣效了


会话 2 ,执⾏选中 s q l


 

mysql临键锁加锁时机_java_43


执⾏成功了


4.4修改索引创建顺序


将创建 b 字段索引提前到创建 a 字段索引之前


mysql临键锁加锁时机_mysql临键锁加锁时机_44


会话 1 ,执⾏选中 s q l


 

mysql临键锁加锁时机_字段_45


会话 2 ,执⾏选中 s q l


 

mysql临键锁加锁时机_字段_46


a=6 时为,⽆阻塞,因此 a>5 没有⽣效


会话 2 ,执⾏选中 s q l


 

mysql临键锁加锁时机_java_47


b=8 时,阻塞了,因此 b<9 ⽣效


总结


表结构字段顺序和条件查询顺序不会影响锁区间依据字段,索引创建顺序影响锁区间依据字段。


5.合并索引和⾮合并索引


会话 1 ,执⾏选中 s q l



mysql临键锁加锁时机_java_48


会话 2 ,执⾏选中 s q l



mysql临键锁加锁时机_字段_49


当 a=6 时,阻塞, a>5 ⽣效


会话 2 ,执⾏选中 s q l



mysql临键锁加锁时机_java_50


当 b=2 时,阻塞, b<9 ⽣效


总结:


索引合并查询,条件字段中的索引都将作为锁区间依据字段。


6.普通字段


会话 1 ,执⾏选中 s q l


 

mysql临键锁加锁时机_mysql临键锁加锁时机_51


会话 2 ,执⾏选中 s q l


 

mysql临键锁加锁时机_字段_52


插⼊ c=6 时,阻塞,因为⾮索引字段查询时全表查询,锁的是全表,因此阻塞全表带有加锁操作。


7.组合索引和两个索引总结


表结构顺序和条件查询顺序不能影响组合索引和两个索引在条件查询时锁区间依据字段,组合索引锁区间依据字段


是创建组合索引时的⾸字段,两个索引锁区间依据字段是各个索引创建的顺序。


题外话:


插⼊数据的时候,先判断唯⼀ ID 是否已经存在,再去尝试加;


使⽤ o r 不⼀定⾛索引,即使⾛也是合并索引


六、临键锁


触发时机:当范围查询命中数据触发临键锁。


临键锁在 mysql8 进⾏了优化:


mysql5.7 版本


如: id>5 and id <10,Id=9 命中数据库数据, 9 左右锁区间为(5, 9],(9,11], 因此锁住的数据范围( 5,11] 。


Mysql8.0.28 版本


如: id>5 and id <10,Id=9 命中数据库数据, 9 左右锁区间为(5, 9),(9,11), ⼜因为命中 9 数据,因此锁住的数据范围 (5,11 )。


1.mysql5.7版本


会话 1 ,执⾏选中 s q l


mysql临键锁加锁时机_java_53


会话 2 ,执⾏对 id=11 数据更新

 

mysql临键锁加锁时机_mysql临键锁加锁时机_54


发⽣了阻塞,符合我们前⾯介绍的。


2.Mysql8.0.28版本


会话 1 ,执⾏ s q l

 

mysql临键锁加锁时机_开发语言_55


会话 2 ,执⾏ s q l

 

mysql临键锁加锁时机_字段_56


会话 2 ,执⾏ s q l ,对 id=9 数据进⾏更新

 

mysql临键锁加锁时机_开发语言_57


会话 2 进⼊阻塞状态


会话 2 再次执⾏ s q l ,对 id=11 数据进⾏更新

 

mysql临键锁加锁时机_java_58


本次会话 2 并没有阻塞,什么⻤?为什么不阻塞,猜测 mysql 开发者对临键锁进⾏了优化。