锁机制
约 1122 字大约 4 分钟
布欧-Lewyon
2026-05-15
首页 › MySQL › 事务与锁(在新窗口打开) › 锁机制
MySQL InnoDB 提供了丰富的锁机制来保证并发安全。理解锁的类型和作用范围,对排查死锁和优化并发性能至关重要。
锁的分类
行级锁:共享锁与排他锁
共享锁(S Lock,Shared Lock)
-- 共享锁:允许其他事务也读,但不允许其他事务写
SELECT * FROM account WHERE id = 1 LOCK IN SHARE MODE;
-- 或者用 SELECT ... FOR SHARE(MySQL 8.0+ 语法)
SELECT * FROM account WHERE id = 1 FOR SHARE;排他锁(X Lock,Exclusive Lock)
-- 排他锁:不让其他事务读或写
SELECT * FROM account WHERE id = 1 FOR UPDATE;
-- UPDATE / DELETE / INSERT 会自动加排他锁
UPDATE account SET balance = 800 WHERE id = 1;
-- 等效于先 SELECT ... FOR UPDATE 再更新S 锁与 X 锁的兼容性:
| S | X | |
|---|---|---|
| S | ✅ 兼容 | ❌ 不兼容 |
| X | ❌ 不兼容 | ❌ 不兼容 |
意向锁(Intention Lock)
意向锁是表级锁,表示事务"打算"在行上加什么锁。
-- 事务 A:要给 id=1 加 S 锁
-- 1. 先给 account 表加 IS 锁(告诉其他事务:我会在某行加 S 锁)
-- 2. 再给 id=1 加 S 锁
SELECT * FROM account WHERE id = 1 LOCK IN SHARE MODE;
-- 事务 B:要给全表加 X 锁
LOCK TABLES account WRITE;
-- MySQL 检查:account 表有没有 IS 或 IX 锁?有 IS → 等待兼容性:
| IS | IX | S | X | |
|---|---|---|---|---|
| IS | ✅ | ✅ | ✅ | ❌ |
| IX | ✅ | ✅ | ❌ | ❌ |
| S | ✅ | ❌ | ✅ | ❌ |
| X | ❌ | ❌ | ❌ | ❌ |
三种行锁算法
Record Lock、Gap Lock、Next-Key Lock 是 InnoDB 行锁的三种实现算法。
Record Lock(记录锁/行锁)
只锁住索引记录本身,不锁间隙:
-- 主键等值查询:只锁 id=1 这一行
SELECT * FROM account WHERE id = 1 FOR UPDATE;
-- Record Lock on (id=1)Gap Lock(间隙锁)
锁住索引记录之间的间隙,防止其他事务在间隙中插入新记录(防止幻读的关键):
-- 间隙范围:(1, 3),即 id 在 1 到 3 之间的间隙
SELECT * FROM account WHERE id BETWEEN 1 AND 3 FOR UPDATE;
-- 其他事务 INSERT id=2 会被阻塞Next-Key Lock(临键锁)
Record Lock + Gap Lock 的组合,锁住一个左开右闭区间 (prev, current]:
-- 假设 account 有 id: 1, 4, 7
-- RR 级别下,SELECT ... FOR UPDATE 无匹配行时:
SELECT * FROM account WHERE id = 5 FOR UPDATE;
-- Next-Key Lock 锁定 (4, 7] 区间
-- 其他事务无法插入 id=5 或 id=6表级锁
显式表锁(不推荐在 InnoDB 中使用)
LOCK TABLES account READ; -- 读锁
LOCK TABLES account WRITE; -- 写锁
UNLOCK TABLES;InnoDB 用行锁即可保证并发,显式表锁会退化为串行,仅用于极少数特殊场景。
元数据锁(Metadata Lock, MDL)
MDL 是 MySQL Server 层自动管理的锁,保护表结构不被并发修改:
-- 事务 A:查询进行中
SELECT * FROM account; -- 加 MDL 读锁
-- 事务 B:修改表结构
ALTER TABLE account ADD COLUMN ...; -- 需要 MDL 写锁,等待 A 释放
-- 如果 A 长时间不提交,B 被阻塞,后续针对 account 的所有操作都会被阻塞长事务是 MDL 阻塞的常见原因。排查:
SHOW PROCESSLIST查看Waiting for table metadata lock。
SELECT ... FOR UPDATE 与 FOR SHARE 的使用场景
-- 场景:下单时扣减库存
-- ✅ 正确做法:加排他锁,防止超卖
START TRANSACTION;
SELECT stock FROM product WHERE id = 1 FOR UPDATE;
-- 锁住这条记录,其他事务无法更新
IF stock > 0:
UPDATE product SET stock = stock - 1 WHERE id = 1;
INSERT INTO order_ (product_id) VALUES (1);
COMMIT;
ELSE:
ROLLBACK;-- 场景:读取当前最新值,但不需要改
SELECT * FROM account WHERE id = 1 FOR SHARE;
-- 加 S 锁,允许其他事务读,阻止其他事务写小结
- InnoDB 行锁有 S(共享)和 X(排他)两种模式;意向锁(IS/IX)是表级信号量。
- 三种行锁实现:Record Lock(锁记录)、Gap Lock(锁间隙)、Next-Key Lock(记录+间隙)。
- RR 级别下,InnoDB 用 Next-Key Lock 防止幻读。
- 表锁(LOCK TABLES)在 InnoDB 中极少使用;MDL 是 Server 层自动管理。
- 长事务和 DDL 冲突是 MDL 阻塞的常见原因。
上一节:隔离级别与 MVCC 下一节:死锁排查
