mysql 加锁案例「1」

216 阅读2分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

明确一下案例的范围:

  1. select ... for update
  2. 查询的执行计划中包含:primary key 或者是 index (二级 index)
  3. 通过 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;
  1. number ⇒ 主键
  2. name ⇒ 二级 index
INSERT INTO hero VALUES
    (1, 'liu', 'shu'),
    (3, 'zhu', 'shu'),
    (8, 'cao', 'wei'),
    (15, 'xun', 'wei'),
    (20, 'sun', 'wu');

然后明确一下我们加锁的一些常识:

  1. 锁定读的执行看成是依次读取扫描区间中的记录 (全表扫描 → [-∞, +∞] ) ,然后对记录加锁
  2. ≤ read committed ,会对当前记录加 record lock
  3. ≥ read repeatale,会对当前记录加 next-key lock

步骤:

  1. 通过查询条件确定记录

  2. 生成锁结构与之对应

  3. 判断 ICP 是否成立

  4. 执行回表 (读取二级索引时,需要此操作) → 将对应的聚簇索引加锁

  5. 判断边界条件是否成立

    1. ≤ read committed,不成立则接触 锁 对应关系
    2. ≥ read repeatale,继续持有
    3. 如果不满足,则向 server 端返回一个 "query end"
  6. server 端判断其他查询条件是否满足。除了 ICP 的条件外,其他条件都是上升到 server 层判断的。成立,则发送最终结果到 client:

    1. ≤ read committed,不成立则接触 锁 对应关系
    2. ≥ read repeatale,继续持有

我们可以看出:

  1. 锁的释放,只能由 server 释放。 因为存储引擎的结构和功能都不径相同,不能把 锁 的释放下降到存储引擎,不然没有额外功能的存储引擎就会造成行为上的不一致
  2. 锁的释放还和 隔离级别 有关系

主键查询

SELECT * FROM hero WHERE number = 8 for update;

SELECT * FROM hero WHERE number = 7 for update;

明确查询的特点:

  1. 精确匹配 ⇒ number = 8
  2. use primary key
  3. 第一个:存在这条数据;第二条:不存在数据

先说 mysql 默认的隔离级别:RR,此时加锁情况是:

  1. number = 8

    1. 因为是 primary key,不可能出现重复,所以 gap-lock 没有必要,不会说出现新的插入导致幻读 → 只会加 record-lock
    2. 故:把 number = 8 这条记录加上 record-lock
  2. number = 7

    1. 因为找不到,所以不会加上 record-lock
    2. 但是也因为没有,所以可能出现幻读 → 在下一条前面加上 gap-lock ⇒ 也就是在 (3, 8] 之间加上 gap-lock