文章插图
图片
这里锁住的是 age 字段的 [18, 28) 这区间 。
结论:查询条件为普通索引,且有值,间隙锁
2.6 普通索引(空值)说明:普通索引,数据不存在 。
执行悲观锁查询:
select * from user where age = 19 for update;
执行插入操作,被锁住了:insert into user values(3, 14, '楼仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
文章插图
图片
这里锁住的是 age 字段的 (18, 28) 这区间 。
结论:查询条件为普通索引,且空值,间隙锁
2.7 索引(范围查询)说明:这里的索引,包括主键索引、唯一索引和普通索引 。
执行悲观锁查询:
select * from user where id > 1 for update;
【select...for update,表锁?行锁?间隙锁?】执行插入操作,被锁住了:insert into user values(3, 14, '楼仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
文章插图
图片
这里其实可以对 id = 1 的数据进行更新,对于其它数据,都被锁?。?锁住的范围是 id 字段的 (1, 4],(4, 8],(8, 正无穷) 区间 。
结论:查询条件为索引 , 且是范围查询,间隙锁 。
2.8 无索引执行悲观锁查询:
select * from user where user_name = "楼仔" for update;
执行插入操作,被锁住了:insert into user values(3, 14, '楼仔小弟', 20);ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
文章插图
这里明显是锁表了,但是为什么锁的信息还是行锁呢,知道的同学 , 可以私我哈~~
结论:查询条件为无索引,表锁 。
03 加锁规则3.1 规律总结我们把上面的结论进行汇总:
文章插图
图片
总结如下规律:
- 当查询条件为主键和唯一索引,当有值时 , 是行锁;
- 当查询条件为主键和唯一索引,当为空值时 , 是间隙锁;
- 当查询条件为普通索引,是间隙锁;
- 当查询条件为索引,且为范围查询,是间隙锁;
- 当查询条件无索引,是表锁 。
为了便于大家理解,我先普及 3 个概念:
- Record Lock:行锁
- Gap Lock:间隙锁 , 锁定一个范围,但不包含记录本身
- Next-Key Lock:行锁 + 间隙锁 , 左开右闭,比如(1,5]
两个“原则”:
- 原则 1:加锁的基本单位是 next-key lock , 其中 next-key lock 是前开后闭区间;
- 原则 2:查找过程中访问到的对象才会加锁 。
- 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁;
- 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁 。
针对我们前面总结的 5 条规律,我们先分析这两条:
- 当查询条件为主键和唯一索引,当有值时 , 是行锁;
- 当查询条件为主键和唯一索引,当为空值时 , 是间隙锁 。
根据 “原则 1”,加锁的基本单位是 next-key lock,当 “索引上为等值查询” 时(即能查到该数据) , 根据 “优化 1”,间隙锁退化为行锁 。