这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战
明确一下案例的范围:
- select ... for update
- 查询的执行计划中包含:primary key 或者是 index (二级 index)
- 通过 index 查询,会使用 condition index push (索引下推)
下面开始案例:
CREATE TABLE hero (
number INT,
name VARCHAR(100),
country varchar(100),
PRIMARY KEY (number),
KEY idx_name (name)
) Engine=InnoDB CHARSET=utf8;
- number ⇒ 主键
- name ⇒ 二级 index
INSERT INTO hero VALUES
(1, 'liu', 'shu'),
(3, 'zhu', 'shu'),
(8, 'cao', 'wei'),
(15, 'xun', 'wei'),
(20, 'sun', 'wu');
然后明确一下我们加锁的一些常识:
- 锁定读的执行看成是依次读取扫描区间中的记录 (全表扫描 → [-∞, +∞] ) ,然后对记录加锁
- ≤ read committed ,会对当前记录加 record lock
- ≥ read repeatale,会对当前记录加 next-key lock
步骤:
-
通过查询条件确定记录
-
生成锁结构与之对应
-
判断 ICP 是否成立
-
执行回表 (读取二级索引时,需要此操作) → 将对应的聚簇索引加锁
-
判断边界条件是否成立
- ≤ read committed,不成立则接触 锁 对应关系
- ≥ read repeatale,继续持有
- 如果不满足,则向 server 端返回一个 "query end"
-
server 端判断其他查询条件是否满足。除了 ICP 的条件外,其他条件都是上升到 server 层判断的。成立,则发送最终结果到 client:
- ≤ read committed,不成立则接触 锁 对应关系
- ≥ read repeatale,继续持有
我们可以看出:
- 锁的释放,只能由 server 释放。 因为存储引擎的结构和功能都不径相同,不能把 锁 的释放下降到存储引擎,不然没有额外功能的存储引擎就会造成行为上的不一致
- 锁的释放还和 隔离级别 有关系
主键查询
SELECT * FROM hero WHERE number = 8 for update;
SELECT * FROM hero WHERE number = 7 for update;
明确查询的特点:
- 精确匹配 ⇒ number = 8
- use primary key
- 第一个:存在这条数据;第二条:不存在数据
先说 mysql 默认的隔离级别:RR,此时加锁情况是:
-
number = 8
- 因为是 primary key,不可能出现重复,所以 gap-lock 没有必要,不会说出现新的插入导致幻读 → 只会加 record-lock
- 故:把 number = 8 这条记录加上 record-lock
-
number = 7
- 因为找不到,所以不会加上 record-lock
- 但是也因为没有,所以可能出现幻读 → 在下一条前面加上 gap-lock ⇒ 也就是在 (3, 8] 之间加上 gap-lock