锁机制、幻读、死锁

一、基础锁:表锁 & 行锁 & 意向锁

1. 表锁

定义:锁定整张数据表,锁住期间其他事务无法对表内数据进行增删改操作。

特性:开销小、加锁快、无死锁;锁粒度最大、并发性能极差。

适用场景:批量全表更新、数据迁移、低频更新高频查询业务。

存储引擎支持:MyISAM 仅支持表锁;InnoDB 支持表锁(手动加锁)。

2. 行锁

定义:InnoDB 专属锁机制,仅锁定查询条件匹配的单行数据。

特性:开销大、加锁慢、会产生死锁;锁粒度最小、数据库并发极高。

生效前提(面试必考)必须命中索引,如果未命中索引、索引失效,行锁会自动升级为表锁。

适用场景:高并发、精准单行更新、线上核心业务读写场景。

3. 意向锁(意向共享 IS / 意向排他 IX)

定义:意向锁是表级锁,由 InnoDB 自动维护,人工无法手动加锁。用于标记「事务即将对表内数据加行锁」。

作用:解决表锁与行锁之间的冲突检测问题,避免遍历全表行锁判断锁冲突,大幅提升锁检测效率。

分类

  • IS(意向共享锁):事务要加行共享锁,先加 IS 表意向锁

  • IX(意向排他锁):事务要加行排他锁,先加 IX 表意向锁

锁兼容规则:意向锁之间互相兼容;表排他锁与所有意向锁互斥。

二、InnoDB 行锁细分:记录锁、间隙锁、临键锁

1. 记录锁(Record Lock)

定义:精准锁定存在的单行索引记录

触发条件:RR 隔离级别下,精准等值匹配唯一索引/主键索引,精确命中已有数据。

作用:防止当前行数据被其他事务修改、删除。

2. 间隙锁(Gap Lock)

定义:锁定索引记录之间的空白区间,不锁定已有数据,只锁住数据间隙。

触发条件

  • 索引 范围查询(> < >= <=)当前读
  • 唯一索引因隐式转换等失效降级为普通索引时,等值查询会走临键锁,附带间隙锁;
  • 等值查询匹配不到任何数据 → 不会加间隙锁。

核心作用:禁止其他事务在锁定间隙内插入新数据,从根源预防幻读。

特点:只会阻塞插入操作,不会阻塞修改、删除操作;仅存在于 RR 隔离级别。

3. 临键锁 / 下一键锁(Next-Key Lock)

定义:InnoDB 在 RR 可重复读隔离级别 下默认的行锁算法,本质是 记录锁 + 间隙锁 的合体;锁区间为 左开右闭 (a, b],同时锁定当前索引记录 + 索引左侧相邻间隙,用来规避幻读。

触发条件:仅在 RR 隔离级别 + 走普通二级索引的当前读 触发。

包含:普通索引等值查询、普通索引范围查询、唯一索引失效遍历查询。

降级机制(重点)

  • 唯一索引 / 主键 等值查询

命中数据,临键锁降级为记录锁。未命中数据,不加锁。

  • 非唯一普通索引等值查询

命中数据,保持临键锁。未命中数据,临键锁降级为间隙锁。

  • 普通二级索引以及唯一索引范围查询

不降级,始终保持完整临键锁。锁定所有数据行及包含的间隙。

  • 未命中索引时(全表扫描)

保持临键锁,扫描的所有行都会加锁,包括这些行中间的间隙,生产环境禁止出现。

面试重点:临键锁是 RR 级别解决幻读的核心机制。

业界公认:InnoDB RR 隔离级别,并没有 100% 彻底解决幻读,只能解决「大部分场景幻读」,解决不了「分两次范围当前读」产生的幻读。

三、RR 隔离级别为什么能解决大部分幻读?

  1. 快照读(普通 select):依靠 MVCC 多版本并发控制,读取 undo log 历史数据快照,不会读取当前事务外的最新插入数据,规避幻读;

  2. 当前读(select … for update / update / delete):依靠 临键锁+间隙锁,锁定查询范围的所有数据及空白间隙,彻底禁止其他事务插入新数据;

双向结合,解决大多数幻读问题

补充:RC 隔离级别无间隙锁、临键锁,且每次查询都会生成新快照,无法解决幻读。

四、死锁全套知识点(高频)

1. 死锁四个必要条件(缺一不可)

  • 互斥条件:锁资源同一时间只能被一个事务占用

  • 请求与保持:事务持有当前锁,同时请求获取其他事务的锁资源

  • 不可剥夺:已获取的锁不能被强制抢占,只能事务主动释放

  • 循环等待:多个事务互相持有对方需要的锁,形成闭环等待

2. 死锁产生场景

多事务交叉更新不同行数据、加锁顺序不一致、范围查询(当前读)触发大量间隙锁互相占用,极易引发死锁。

3. 死锁排查与解决方法

排查命令:执行 show engine innodb status;,查看 LATEST DETECTED DEADLOCK 模块,可查看最近一次死锁的事务、锁资源、SQL、等待信息。

解决方案

  • 统一业务加锁顺序,避免循环等待

  • 缩短事务执行时长,及时提交释放锁资源

  • 减少范围查询,避免生成大量间隙锁

  • 合理设置事务超时参数 innodb_lock_wait_timeout

  • 业务层面做幂等、锁降级,减少行锁竞争