锁机制、幻读、死锁
一、基础锁:表锁 & 行锁 & 意向锁
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 隔离级别为什么能解决大部分幻读?
-
快照读(普通 select):依靠 MVCC 多版本并发控制,读取 undo log 历史数据快照,不会读取当前事务外的最新插入数据,规避幻读;
-
当前读(select … for update / update / delete):依靠 临键锁+间隙锁,锁定查询范围的所有数据及空白间隙,彻底禁止其他事务插入新数据;
双向结合,解决大多数幻读问题。
补充:RC 隔离级别无间隙锁、临键锁,且每次查询都会生成新快照,无法解决幻读。
四、死锁全套知识点(高频)
1. 死锁四个必要条件(缺一不可)
-
互斥条件:锁资源同一时间只能被一个事务占用
-
请求与保持:事务持有当前锁,同时请求获取其他事务的锁资源
-
不可剥夺:已获取的锁不能被强制抢占,只能事务主动释放
-
循环等待:多个事务互相持有对方需要的锁,形成闭环等待
2. 死锁产生场景
多事务交叉更新不同行数据、加锁顺序不一致、范围查询(当前读)触发大量间隙锁互相占用,极易引发死锁。
3. 死锁排查与解决方法
排查命令:执行 show engine innodb status;,查看 LATEST DETECTED DEADLOCK 模块,可查看最近一次死锁的事务、锁资源、SQL、等待信息。
解决方案
-
统一业务加锁顺序,避免循环等待
-
缩短事务执行时长,及时提交释放锁资源
-
减少范围查询,避免生成大量间隙锁
-
合理设置事务超时参数 innodb_lock_wait_timeout
-
业务层面做幂等、锁降级,减少行锁竞争