【mysql】锁机制 - 2.行锁间隙锁临键锁

目录

1. 锁的几种类型

1.1 记录锁(行锁)?Record Lock

1.2?间隙锁 Gap Lock

1.3 临键锁?Next-Key Lock

2. 加锁过程

2.1 唯一索引的查询

(1) 等值查询, 查询记录存在

(2)?等值查询, 查询记录不存在

(3) 范围查询

2.2 普通索引的查询

(1) 等值查询, 查询记录存在

(2) 等值查询, 查询记录不存在

(3) 范围查询

2.3 其他查询

2.4 总结


1. 锁的几种类型

对 InnoDB 按照锁粒度可以分为:行锁、间隙锁、临键锁、意向锁

1.1 记录锁(行锁)Record Lock

即锁住一条记录。 一般在使用主键或者唯一索引进行查找时体现。

  • 分为两种模式:共享和独占,即 S 共享锁、X 排他锁

  • 行锁依附于索引而存在,也就是说, 行锁是在加索引上而不是行上的

  • InnoDB 使用的是聚簇索引,因此 行锁最终都会落到聚簇索引上

比如:

select * from table1 where id=5 for update; ? ? ? ? ?-- 会在 id=5 的索引记录上加 X 锁
select * from table1 where id=5 lock in share mode;??-- 会在 id=5 的索引记录上加 S 锁
select * from table1 where id=5; ??? ??? ??? ??? ?? ?-- 不会加任何锁

1.2间隙锁 Gap Lock

即对行记录之间的间隙加锁。 通常在不使用唯一索引进行范围查找时出现。

  • 间隙锁没有共享和排他的概念,其目的是防止其他事务的 insert 操作, 规避幻读问题

  • 锁定的是一个区间,左开右开,如 index ∈ (10,20)

  • 间隙锁也是依附于索引而存在

比如:

-- 表数据有 id = 1,3,5
select * from table1 where id>3 and id<5 for update; ?-- 会锁住 (3,5) 范围, 防止插入4

1.3 临键锁Next-Key Lock

是间隙锁 + 右边界的行锁。

  • 锁定的是一个区间, 左开右闭,如 index ∈ (10,20]

  • 比如一个索引包含值 10, 15, 20, 那有临键锁:(-oo, 10] (10, 15](15, 20](20, oo)

比如:

-- 表数据有 id = 1,3,5
select * from table1 where id>3 and id<=5 for update; ?-- 会锁住 (3,5] 范围

2. 加锁过程

加锁的基本单位是临键锁

无论什么情况下, InnoDB 会 往前扫描 到第一个不满足条件的行为止。

2.1 唯一索引的查询

比如有以下表结构:

id

a

10

1

20

3

(1) 等值查询, 查询记录存在

当查询记录存在,在用「唯一索引进行等值查询」时,临键锁会退化成「行锁」

select * from table1 where id = 20 for update;

加锁过程:

  • 加锁的基本单位是临键锁,因此加锁范围在 (10, 20]

  • 值存在,所以退化成行锁,即对 id = 20 加锁

(2)等值查询, 查询记录不存在

当查询记录不存在,在用「唯一索引进行等值查询」时,临键锁会退化成「间隙锁」

select * from table1 where id = 15 for update;
insert into table1 set (id) values (11);

加锁过程:

  • 加锁的基本单位是临键锁,因此加锁范围在 (10, 20]

  • 值不存在,所以退化成间隙锁,即锁住 (10, 20)

此时的 insert 操作会阻塞,因为 11 在 (10, 20) 范围内,已经被锁住了

(3) 范围查询
select * from table1 where id >= 10 and id < 11 for update;
insert into table1 set (id) values (15);

加锁过程:

  • 对 id=10 条件, 会对 id=10 这一条记录加行锁

  • 由于是范围查询,会继续向后查找到 id=20, 加锁范围在 (10, 20],再退化成间隙锁 (10, 20)

  • 因此最终锁住的是 id=10 和 (10, 20)

此时的 insert 操作会阻塞,因为 15 在 (10, 20) 范围内,已经被锁住了

2.2 普通索引的查询

比如有以下表结构:

id

a(非唯一索引)

10

1

20

3

30

5

(1) 等值查询, 查询记录存在

当查询记录存在,对非主键索引会加「 临键锁」和「 间隙锁」 两把锁,锁不会有退化行为,并对主键索引加「行 锁」

select * from table1 where a = 3 for update;
insert into table1 set (a) values (4);

加锁过程:

  • 加锁的基本单位是临键锁,因此加锁范围在 a ∈ (1, 3],但不会退化为行锁

  • 对 a=3 对应的主键 id=20 加行锁

  • 由于是非唯一索引,且查询记录存在,所以还会加上间隙锁, 规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是 (3, 5)

  • 因此最终锁住的是非主键索引 a∈ (1, 3] 和 (3, 5) 和主键索引 id = 20

此时的 insert 操作会阻塞,因为 4 在 (3, 5) 范围内,已经被锁住了

(2) 等值查询, 查询记录不存在

当查询记录不存在,对非主键索引加「 临键锁」 ,然后会退化为「 间隙锁」,对主键索引不加锁

select * from table1 where a = 2 for update;
insert into table1 set (a) values (4);

加锁过程:

  • 加锁的基本单位是临键锁,因此加锁范围在 a ∈ (1, 3]

  • 由于是非唯一索引,且查询记录不存在,所以临键锁退化为间隙锁,即 (1, 3)

  • 因此最终锁住的是非主键索引 a∈ (1, 3),对主键索引不会加锁

此时的 insert 操作正常执行不会阻塞。

(3) 范围查询
select * from table1 where a >= 3 and a < 4 for update;
insert into table1 set (a) values (5);

加锁过程:

  • 对条件 a=3,会先加临键锁 (1, 3],但不会退化为行锁

  • 对 a=3 对应的主键 id=20 加行锁

  • 对向下的一条主键 id=30 加行锁

  • 继续向后查找直到 a = 5,加临键锁 (3,5],但不会退化为间隙锁

  • 因此最终锁住的是非主键索引 a∈ (1, 3](3, 5] 和主键索引 id=20, 30

此时的 insert 操作会阻塞,因为 5已经被锁住了

2.3 其他查询

没有索引的查询:

会导致全表扫描,对每一个索引加临键锁,相当于锁住整个表。

Delete语句:

和 select for update / update 语句没有差异,因为 mysql 的 delete 是软删除,先在记录上打一个删除标记,再通过后台的 purge 线程来清理,所以 delete 操作的加锁过程和基本查询是一样的。

2.4 总结

查询规则:

  • 唯一索引等值查询:

    • 当查询的记录存在, 临键锁 会退化成行锁

    • 当查询的记录不存在,临键锁会退化成间隙锁

  • 非唯一索引等值查询:

    • 当查询的记录存在,会加 临键锁和 间隙锁两把锁

    • 当查询的记录不存在,只会加 临键锁,然后退化为间隙锁

  • 非唯一索引和主键索引的范围查询的区别:

    • 唯一索引在满足条件时, 临键锁 退化为间隙锁和记录锁

    • 非唯一索引范围查询, 临键锁不会退化 为间隙锁和记录锁

在使用时要注意:

  • 在执行 update / delete / select for update 语句时,一定要检查语句是否走了索引,避免全表扫描

  • Delete 时尽量使用主键 ID(唯一索引) 删除

  • 查询时尽量使用唯一索引进行查询,锁定范围较小

参考:

MySQL记录锁、间隙锁、临键锁(Next-Key Locks)加锁过程 - caibaotimes - 博客园

MySQL记录锁、间隙锁、临键锁 - DiligentCoder - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值