文章目录
- [每日一篇高频面试题系列之【MySQL 锁】](#每日一篇高频面试题系列之【MySQL 锁】)
-
- 前言
- 一、面试经典原题
- [二、MySQL 锁完整体系分类](#二、MySQL 锁完整体系分类)
- [三、每种锁原理 + 实现方式 + 使用场景](#三、每种锁原理 + 实现方式 + 使用场景)
- 四、高频必背面试问答题
-
- [1. InnoDB 行锁为什么依赖索引?](#1. InnoDB 行锁为什么依赖索引?)
- [2. 哪些情况行锁会降级为表锁?](#2. 哪些情况行锁会降级为表锁?)
- [3. RC 和 RR 在锁上最大区别?](#3. RC 和 RR 在锁上最大区别?)
- [4. 临键锁什么时候降级为记录锁?](#4. 临键锁什么时候降级为记录锁?)
- [5. MySQL 死锁产生的四个必要条件](#5. MySQL 死锁产生的四个必要条件)
- [6. 如何避免 MySQL 死锁?](#6. 如何避免 MySQL 死锁?)
- [7. 怎么排查 MySQL 死锁?](#7. 怎么排查 MySQL 死锁?)
- [8. 意向锁能不能人工手动加?](#8. 意向锁能不能人工手动加?)
- [9. 共享锁和排他锁能否混用?](#9. 共享锁和排他锁能否混用?)
- [10. 间隙锁会不会阻塞 update、delete?](#10. 间隙锁会不会阻塞 update、delete?)
- [五、30 秒面试万能背诵版](#五、30 秒面试万能背诵版)
- 六、小测验
每日一篇高频面试题系列之【MySQL 锁】
适合:春招 / 跳槽 / 转行 | 级别:Java 中初级开发 | 难度:⭐⭐⭐⭐ | 频率:极高
前言
关注公众号 "知识汲取者"【Java 面试题】合集,可以每天抽空五分钟,趁着坐车、蹲坑、摸鱼等琐碎时间看一看,一来可以扎实基本功 ,二来可以随时熟悉面试题,让我们始终保持拥有随时可面的状态,时刻保持危机意识。
本专栏适合人群:
- 在职的 Java 开发
- 准备或正在跳槽的 Java 开发
- 未来想从事 Java 开发
欢迎关注公众号
一、面试经典原题
- MySQL InnoDB 有哪些锁?分类有哪些?
- 表锁、行锁、意向锁、间隙锁、临键锁原理、实现方式和区别?
- 行锁什么时候会降级为表锁?
- 共享锁、排他锁、意向锁兼容规则?
- 间隙锁、临键锁解决了什么问题?RC 和 RR 下有什么区别?
- MySQL 死锁产生原因、如何避免、怎么排查?
PS:如果你对于上述 MySQL 的高频面试题不是特别熟悉,可以通过阅读二、三章节进行学习,同时完成四、六章节的题目,如果觉得二、三章节内容太多记不住,想快速记忆就阅读五章节
二、MySQL 锁完整体系分类
1. 按锁粒度分
-
表级锁
- 核心特性:锁定整张表。开销小、加锁快,不会出现死锁,但锁粒度最大,发生锁冲突的概率最高,并发度最低
- 触发方式 :通过
LOCK TABLES 表名 READ/WRITE显式加锁;或在执行ALTER TABLE、DROP TABLE等 DDL 语句时由系统自动触发 - 适用场景:适用于全表扫描、批量更新整张表数据,或者对并发要求不高的离线业务处理
-
行级锁
- 核心特性:锁定单行或多行索引记录。开销大、加锁慢,可能出现死锁,但锁粒度最小,发生锁冲突的概率最低,并发度最高
- 触发方式 :在执行
SELECT ... FOR UPDATE、INSERT、UPDATE、DELETE等语句时,通过索引条件精准定位并自动触发 - 适用场景:高并发的 OLTP(在线事务处理)系统,如电商下单、库存扣减、账户转账等核心业务
-
页级锁
- 核心特性:锁定相邻的一组记录(数据页)。开销和加锁时间介于表锁和行锁之间,会出现死锁,并发度一般
- 触发方式:由存储引擎(如早期的 BerkeleyDB)在操作数据页时自动触发
- 适用场景:现代 MySQL 业务中已极少使用,多见于一些特定的历史存储引擎或特殊场景
2. 按锁(读写)类型分
-
共享锁(S 锁 / 读锁)
- 核心特性:遵循"大家一起读,不准改"的原则。允许多个事务同时对同一份数据加 S 锁并读取(读读兼容);但只要有一个事务持有 S 锁,其他事务就无法对该数据加排他锁(X锁),即不能修改(读写互斥)
- 触发方式 :通过手动执行
SELECT ... LOCK IN SHARE MODE(或 MySQL 8.0+ 的SELECT ... FOR SHARE)显式添加 - 适用场景:需要保证在读取数据期间,数据不会被其他事务修改,但允许其他人同时读取的场景(如读取并校验关键配置数据)
-
排他锁(X 锁 / 写锁)
- 核心特性:遵循"我一个人改,别人不准碰"的原则。同一时间只能有一个事务对某份数据加 X 锁。一旦加上,其他任何事务都无法再对该数据加 S 锁(不能读)或 X 锁(不能改),完全独占数据
- 触发方式 :所有的写操作(
INSERT、UPDATE、DELETE)会自动加上 X 锁;也可以通过手动执行SELECT ... FOR UPDATE显式添加 - 适用场景:涉及数据修改、删除或需要强一致性独占读取的场景(如秒杀扣库存、余额修改)
3. 按锁算法分(InoDB核心)
-
记录锁(Record Lock)
- 核心特性:最基础的行级锁,精准锁住某一条索引记录,确保在锁持有期间,其他事务无法修改或删除该条记录
- 触发方式 :在唯一索引(如主键)上进行等值查询并加锁(如
WHERE id = 1 FOR UPDATE)时触发 - 适用场景:精准更新或删除某一条特定记录的业务操作
-
间隙锁(Gap Lock)
- 核心特性:锁住索引记录之间的间隙(左开右开区间),不锁记录本身。核心目的是防止其他事务在这个间隙中插入新记录,从而彻底解决"幻读"问题
- 触发方式 :在普通索引进行等值或范围查询,以及主键/唯一索引进行范围查询(如
BETWEEN、>)时触发 - 适用场景:在可重复读(RR)隔离级别下,防止范围查询时出现幻读,保证数据的一致性视图
-
临键锁(Next-Key Lock)
- 核心特性:记录锁 + 间隙锁的组合,锁定左开右闭区间。这是 InnoDB 在 RR 隔离级别下的默认行锁算法,既锁住记录本身,又锁住前面的间隙
- 触发方式:InnoDB 在 RR 隔离级别下进行范围查询或普通索引查询时,默认自动使用此算法加锁
- 适用场景:MySQL 默认的 RR 隔离级别下,绝大多数涉及索引范围扫描的加锁场景,从根源上杜绝幻读
4. 意向锁(表级辅助锁)
-
意向共享锁(IS)
- 核心特性:一种表级别的"预告锁",表示事务意图对表中的某些行加共享锁(S锁)。它与表级共享锁兼容,与表级排他锁互斥
- 触发方式 :在执行
SELECT ... LOCK IN SHARE MODE前,数据库会自动先在表级别加上 IS 锁 - 适用场景:提高加表锁时的效率。让数据库在需要给整张表加锁时,只需检查表上有没有意向锁,而无需遍历全表检查每一行
-
意向排他锁(IX)
- 核心特性:一种表级别的"预告锁",表示事务意图对表中的某些行加排他锁(X锁)。它与任何表级锁(S锁和X锁)都互斥
- 触发方式 :在执行
SELECT ... FOR UPDATE、INSERT、UPDATE、DELETE前,会自动加上 IX 锁 - 适用场景:同 IS 锁,主要用于快速判断表内是否有行被加锁,避免全表扫描带来的性能损耗
5. 其他高频锁
-
自增锁(AUTO-INC)
- 核心特性:一种特殊的表级锁。在插入数据时锁定自增计数器,保证生成的自增值是唯一且连续的(在高并发模式下会采用轻量级锁来平衡性能与连续性)
- 触发方式:当向带有自增主键(AUTO_INCREMENT)的表中插入数据时自动触发
- 适用场景:依赖自增主键生成唯一连续 ID 的表插入操作
-
元数据锁(MDL)
- 核心特性:由 MySQL Server 层管理的锁,用于保护表结构等元数据信息。DML 操作加 MDL 读锁,DDL 操作加 MDL 写锁,确保读写互斥、写写互斥
- 触发方式:对表进行任何增删改查(DML)或表结构变更(DDL)时,由系统自动触发
- 适用场景 :防止在查询或修改数据时,表结构被意外修改(如查询时有人执行了
ALTER TABLE),保障元数据的安全与一致性
6.显示锁与隐式锁
在 MySQL 的锁机制中,我们可以根据**"是否需要开发者手动写 SQL 语句干预",将这些锁清晰地划分为"开发者可主动操作"的 显式锁和"数据库自动管理"的隐式锁**两大类
- 显示锁:这类锁需要你在写 SQL 语句时,通过特定的语法手动加上。通常用于在复杂业务中,为了保证数据的一致性而进行提前加锁(悲观锁)
- 隐式锁:这类锁由 MySQL 存储引擎(InnoDB)或 Server 层在后台自动维护,开发者无法直接通过 SQL 命令去"加"或"释放"它们,只需要了解它们的触发机制即可
总结
| 锁分类维度 | 锁名称 | 核心特性 | 触发方式 | 适用场景 | 操作属性 |
|---|---|---|---|---|---|
| 按锁粒度 | 表级锁 | 锁定整张表,开销小、无死锁,但并发度最低 | LOCK TABLES 显式加锁;或执行 DDL 语句自动触发 |
全表扫描、批量更新、离线业务处理 | 可主动操作 |
| 行级锁 | 锁定单行/多行,开销大、有死锁,但并发度最高 | 通过索引条件精准定位,执行增删改查时自动触发 | 高并发的 OLTP 系统(如电商下单、转账) | 数据库自动管理 | |
| 页级锁 | 锁定数据页,开销和并发度介于表锁与行锁之间 | 由特定存储引擎在操作数据页时自动触发 | 现代业务极少使用(多见于早期存储引擎) | 数据库自动管理 | |
| 按读写类型 | 共享锁 (S锁) | "大家一起读,不准改",读读兼容,读写互斥 | SELECT ... LOCK IN SHARE MODE / FOR SHARE |
读取并校验关键数据,防止读取期间被篡改 | 可主动操作 |
| 排他锁 (X锁) | "我一个人改,别人不准碰",写读、写写均互斥 | INSERT/UPDATE/DELETE 自动触发;或 SELECT ... FOR UPDATE |
数据修改、删除,或需独占读取的防并发场景 | 可主动操作/自动管理 | |
| 按锁算法 | 记录锁 | 精准锁住某一条索引记录 | 在唯一索引上进行等值查询并加锁时触发 | 精准更新或删除某一条特定记录 | 数据库自动管理 |
| 间隙锁 | 锁住索引之间的间隙(左开右开),防插入 | 普通索引等值/范围查询,或主键范围查询时触发 | RR 隔离级别下,防止范围查询出现幻读 | 数据库自动管理 | |
| 临键锁 | 记录锁+间隙锁(左开右闭),RR级别默认算法 | RR 隔离级别下进行范围或普通索引查询时默认使用 | 绝大多数涉及索引范围扫描的加锁场景 | 数据库自动管理 | |
| 意向锁 | 意向共享锁(IS) | 表级"预告锁",意图对某些行加 S 锁 | 执行 SELECT ... LOCK IN SHARE MODE 前自动加锁 |
提升加表锁时的效率,避免全表遍历检查 | 数据库自动管理 |
| 意向排他锁(IX) | 表级"预告锁",意图对某些行加 X 锁 | 执行 SELECT ... FOR UPDATE、增删改前自动加锁 |
提升加表锁时的效率,避免全表遍历检查 | 数据库自动管理 | |
| 其他高频锁 | 自增锁 | 特殊的表级锁,锁定自增计数器保证唯一连续 | 向带自增主键的表插入数据时自动触发 | 依赖自增主键生成唯一连续 ID 的插入操作 | 数据库自动管理 |
| 元数据锁(MDL) | Server层锁,保护表结构等元数据,防读写冲突 | 访问表(DML)或修改表结构(DDL)时自动触发 | 防止查询时表结构被意外修改,保障元数据安全 | 数据库自动管理 |
三、每种锁原理 + 实现方式 + 使用场景
1、表锁
-
实现方式
- 作用在整张数据表,不依赖索引
- MyISAM 默认引擎锁
- InnoDB 手动可加:
lock tables 表 read/write
-
特点
- 加锁快、开销小、无死锁
- 粒度大、并发低
- 写锁阻塞所有读写
-
适用场景:全表备份、批量导入、DDL 变更、临时全局锁控制
2、行锁(记录锁)
-
实现方式
- InnoDB 基于索引实现
- 只锁定索引对应的行记录
- 不走索引 → 行锁直接降级为表锁
-
触发 SQL
sql-- 排他行锁 select * from t where id=1 for update; update t set name='' where id=1; delete t where id=1; -- 共享行锁 select * from t where id=1 lock in share mode; -
特点
- 粒度最小、并发最高
- 依赖主键 / 唯一索引生效
- 容易产生死锁
3、共享锁 S / 排他锁 X
-
共享锁 S(读锁)
- 事务加读锁,自己可读、可加共享锁
- 其他事务可读、不能加写锁
- 语法:
lock in share mode
-
排他锁 X(写锁)
- 独占锁,自己可读写
- 其他事务既不能读也不能写
- update、delete、for update 自动加 X 锁
-
兼容规则(面试必背)
S 共享锁 X 排他锁 S 兼容 互斥 X 互斥 互斥
4、意向锁 IS / IX
-
实现方式
- 属于表级锁,自动由 InnoDB 维护,人工不能手动加
- 事务准备加行锁前,先在表上加意向锁
-
作用:不用遍历全表每一行判断锁状态,快速判断表是否有冲突表锁,提升加锁效率。
- IS:意向共享,准备给行加 S 锁
- IX:意向排他,准备给行加 X 锁
-
兼容规则
- 意向锁之间互相兼容
- 只要表上有 S/X 表锁,就阻塞后续所有 IS/IX
5、间隙锁 Gap Lock
-
实现方式
- 仅 RR 可重复读 隔离级别存在
- RC 级别关闭间隙锁
- 锁定两条索引之间的空隙,不锁定已有记录
-
核心作用 :阻止其他事务在间隙
insert新数据,规避幻读 -
触发场景:范围查询 + 当前读(for update /update 范围条件)
6、临键锁 Next-Key Lock
-
实现方式
- InnoDB RR 默认锁算法
- 结构:
临键锁 = 记录锁 + 间隙锁 - 锁住左开右闭索引区间
-
作用 :既锁住已有行,又锁住前后间隙,最大程度抑制幻读。
-
什么时候退化成普通行锁?
条件命中唯一索引 / 主键 精准等值匹配,临键锁降级为单纯记录锁,不再加间隙锁。
7、MDL 元数据锁
-
实现方式
- 访问表时自动加,保护表结构不被中途修改。
- 读 MDL:多个事务共享
- 写 MDL:排他,阻塞所有读写
-
面试高频坑:长事务不提交,持有 MDL 读锁,导致 DDL 一直阻塞。
8、AUTO-INC 自增锁
- 实现
- 插入自增列时加锁,保证自增值连续唯一。
- 批量插入有不同锁策略,影响性能和自增间隙。
四、高频必背面试问答题
1. InnoDB 行锁为什么依赖索引?
行锁是索引锁,MySQL 必须通过索引定位到具体某一行;
如果没有索引,无法精准锁定行,只能锁整张表,行锁降级为表锁。
2. 哪些情况行锁会降级为表锁?
- 没有命中任何索引
- 索引失效(函数、隐式类型转换、like % 前缀)
- 范围查询不走唯一索引,临键锁范围过大,等价锁全表
3. RC 和 RR 在锁上最大区别?
- RC:无间隙锁、无临键锁,只有记录锁;每次查询刷新快照,无法防幻读
- RR:有间隙锁 + 临键锁,配合 MVCC,最大程度规避幻读
4. 临键锁什么时候降级为记录锁?
精准等值匹配唯一索引 / 主键,直接锁定单行,不需要间隙保护,退化成普通行锁。
5. MySQL 死锁产生的四个必要条件
- 互斥条件
- 不可剥夺
- 请求保持
- 循环等待
6. 如何避免 MySQL 死锁?
- 统一访问表和行的顺序
- 小事务,尽早提交
- 避免长事务
- 合理加索引,缩小锁范围
- 业务加分布式锁替代数据库锁
7. 怎么排查 MySQL 死锁?
- 开启死锁日志:
show engine innodb status; - 查看 LATEST DETECTED DEADLOCK
- 分析两条事务加锁顺序、SQL、索引情况
8. 意向锁能不能人工手动加?
不能,InnoDB 内部自动维护,用户无法手动操作意向锁。
9. 共享锁和排他锁能否混用?
可以,但遵循兼容规则;一旦有一方加 X 锁,全部互斥阻塞。
10. 间隙锁会不会阻塞 update、delete?
不会,间隙锁只锁空隙,不锁已有记录;
已有行的修改不受间隙锁影响,只阻塞 insert。
五、30 秒面试万能背诵版
-
按粒度分表锁、行锁;
-
按算法分记录锁、间隙锁、临键锁,还有意向锁、MDL、自增锁
-
表锁粒度大并发低;行锁基于索引,无索引会降级表锁;
-
共享锁可读不可改,排他锁独占读写;
-
意向锁是表级辅助锁,自动维护,加速锁冲突判断;
-
RR 级别有间隙锁和临键锁,间隙锁锁空隙防插入,临键锁是行锁加间隙锁,规避幻读;
-
RC 没有间隙锁;统一访问顺序、短事务、合理建索引可以避免死锁。
六、小测验
- 简述表锁、行锁、意向锁、间隙锁、临键锁区别?
- 什么情况下行锁会降级为表锁?
- RC 和 RR 在间隙锁、临键锁上有什么差异?
- 死锁产生四个条件?如何排查和避免?
- 临键锁什么时候会退化成普通记录锁?
点赞 + 收藏,每天 5 分钟,面试稳稳上岸 ✨