题目
请介绍 mysql 中有哪些锁类型,以及它们各自的作用和使用场景。
参考答案
开篇先打个预防针
mysql 的锁机制,说简单也简单,说复杂嘛——复杂到能单独写一本书。面试的时候如果你一上来就背"全局锁、表级锁、行级锁......",面试官大概率会微微一笑,然后追问一句:"那行级锁又有哪几种?"
所以咱换个思路——从大到小,逐层剥洋葱。先看大局,再钻细节,保证你自己讲得清、对方听得懂。
一、按粒度分层——三大"祖宗锁"
mysql 的锁如果按照锁的范围来分,从上到下有三个层级。就像公司的管理架构——管全公司的、管部门的、管个人的。

1. 全局锁——"整库冻结"
flush tables with read lock; -- 全局读锁,整个数据库只读 unlock tables; -- 释放
使用场景:全库逻辑备份。现在有了 mysqldump --single-transaction,基本不用这招了。面试里提一嘴"我知道有这么个东西,但 mvcc 时代有了更好的替代方案"就够了。
2. 表级锁——"这张表我包了"
lock tables user read; -- 表级读锁(共享锁) lock tables user write; -- 表级写锁(排他锁) unlock tables;
表锁的优点是开销小、不会死锁(一次性拿锁,没有逐行争抢)。缺点是并发低——一个人锁了整张表,其他人全得排队。
除了显式的 lock tables,还有两类隐式的表级锁很常见:
元数据锁(mdl,metadata lock):mysql 5.5 引入,不需要你手动加。crud 时自动加 mdl 读锁,改表结构(alter table)时自动加 mdl 写锁。如果你在线上执行了一个大表 ddl 被阻塞,八成就是有人在跑慢查询占着 mdl 读锁不撒手。
自增锁(auto-inc lock):跟自增主键(auto_increment)配合使用。插入数据时自动加,保证自增值不会重复。mysql 8.0 后有了轻量级自增锁,性能好很多。
3. 行级锁——"精准制导"
innodb 才支持行锁,myisam 是不行的——这也是一般项目都选 innodb 的核心原因之一。
行锁由存储引擎层实现,开销比较大(每行都要维护锁信息),但并发度高。行锁下面还有细分,这是面试重灾区,我们稍后展开。
二、按读写属性分——共享锁 vs 排他锁
这俩概念贯穿 mysql 所有锁类型,先搞明白它们:
| 类型 | 别称 | s/x 缩写 | 一句话 |
|---|---|---|---|
| 共享锁(shared lock) | 读锁 | s 锁 | 你能读,我也能读,但谁也不能写 |
| 排他锁(exclusive lock) | 写锁 / 互斥锁 | x 锁 | 我独占,别人读也不行、写也不行 |
兼容矩阵(这是面试里能画出来的加分项):
| s 锁 | x 锁 | |
|---|---|---|
| s 锁 | ✅ 兼容 | ❌ 冲突 |
| x 锁 | ❌ 冲突 | ❌ 冲突 |
-- 加共享锁(s 锁) select * from user where id = 1 lock in share mode; -- mysql 8.0 改名了: select * from user where id = 1 for share; -- 推荐写法 -- 加排他锁(x 锁) select * from user where id = 1 for update;
一个很实用的面试小口诀:s 锁可以共存,x 锁六亲不认。
三、行级锁的细分类——面试高发区
这里是面试官最爱深挖的地方,也是 间隙锁文档 中详细讲过的内容。这里做个整体梳理:
行级锁 ├── record lock(记录锁) → 锁住一条具体的索引记录 ├── gap lock(间隙锁) → 锁住记录之间的空隙,防插入 ├── next-key lock(临键锁) → record + gap,前开后闭区间 └── insert intention lock → 插入意向锁,特殊的间隙锁
record lock(记录锁)——最老实的那种
-- 锁住 id=10 这一行 select * from user where id = 10 for update;
锁住索引上的一条记录。如果表没有索引,innodb 会隐式创建聚簇索引来做行锁。
注意:行锁是加在索引上的,不是加在数据行上的。这句话面试说出来,直接上分。
gap lock(间隙锁)——防止幻读的功臣
锁住索引记录之间的空隙,防止别人在这些空隙里插入数据。
间隙锁只在 rr(可重复读) 隔离级别下生效。rc 级别下间隙锁直接退场。详情见 上一篇文章。
next-key lock(临键锁)——innodb 的默认行锁
数据分布: 1 5 10 15
索引: ●——(1,5)——●——(5,10)——●——(10,15)——●——(15,+∞)
next-key lock = 区间 + 右端点记录
(1,5] (5,10] (10,15] (15,+∞]默认情况下,innodb 加的锁都是 next-key lock。在某些情况下会退化:
- 唯一索引等值查询且记录存在 → 退化为 record lock
- 唯一索引等值查询且记录不存在 → 退化为 gap lock
insert intention lock(插入意向锁)——隐形的排队哨兵
这是一种特殊的间隙锁。当你要往某个间隙插入数据时,会先在这个间隙上加一个"插入意向锁"。它跟间隙锁的规则是:
多个事务在同一个间隙加插入意向锁 → 互不冲突 插入意向锁遇到间隙锁 → 阻塞
说白了,插入意向锁的作用是让同间隙的插入操作能并发,但又保证在有人锁住整个间隙时插不进去。
四、按锁定方式分——隐式锁 vs 显式锁
| 类型 | 触发方式 | 示例 |
|---|---|---|
| 显式锁 | 你手动加的 | select ... for update、lock tables |
| 隐式锁 | mysql 自动加的 | dml 语句自动加行级 x 锁、mdl 锁、自增锁等 |
大多数时候你不需要操心隐式锁,mysql 替你打理得很好。但了解它有个好处——排查线上锁问题的时候,你知道那些"看不见但确实存在的锁"在哪。
五、一张全景脑图
mysql 锁体系
│
├── 按粒度分层
│ ├── 全局锁(flush tables with read lock)
│ ├── 表级锁
│ │ ├── 表锁(lock tables ... read/write)
│ │ ├── 元数据锁 mdl(自动,隐式)
│ │ └── 自增锁 auto-inc(自动,隐式)
│ └── 行级锁(innodb)
│ ├── record lock(记录锁)
│ ├── gap lock(间隙锁)
│ ├── next-key lock(临键锁,默认)
│ └── insert intention lock(插入意向锁)
│
└── 按读写属性分
├── 共享锁(s 锁,读锁)
└── 排他锁(x 锁,写锁)六、常见面试追问
q1:innodb 行锁是加在数据行上吗?
不是。行锁是加在索引上的。如果你的一条 update 语句的 where 条件不走索引,那 innodb 没办法精准定位到具体行,就会锁住整张表的所有行(实际是锁住所有行的聚簇索引记录),效果等同于表锁。
这也是为什么不走索引的 dml 在 rr 级别下尤其危险。
q2:myisam 和 innodb 在锁上的核心区别?
| myisam | innodb | |
|---|---|---|
| 行级锁 | ❌ 不支持 | ✅ 支持 |
| 事务 | ❌ 不支持 | ✅ 支持 |
| 默认锁 | 表锁 | 行锁(next-key lock) |
| 读-读 | 不阻塞 | 不阻塞 |
| 读-写 | 写优先,读被阻塞 | 通过 mvcc 读不阻塞写 |
一句话:innodb 能活到今天是靠 mvcc + 行锁这套组合拳。
q3:mysql 怎么检测死锁?
innodb 内部维护了一个等待图(wait-for graph),当节点间形成环时即死锁。innodb 会主动检测并回滚其中一个事务。可以通过 show engine innodb status\g 查看最近的死锁信息。
七、一句话总结
mysql 的锁,从上到下管全局、管表、管行;从读写属性分共享和排他;行锁再细分记录、间隙、临键、插入意向。记住这个三层结构,任何锁问题都能往里套。
到此这篇关于mysql的锁类型有哪些的文章就介绍到这了,更多相关mysql的锁类型内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论