mysql意向锁是做什么的_mysql锁协作机制说明

来源:这里教程网 时间:2026-02-28 20:38:44 作者:

意向锁是用来避免全表扫描行锁状态的“预告灯”

当事务想对某几行加

S
锁(
SELECT ... LOCK IN SHARE MODE
)或
X
锁(
SELECT ... FOR UPDATE
)时,InnoDB 会**自动先在表级别加一个意向锁**:加行共享锁前加
IS
锁,加行排他锁前加
IX
锁。这不是你手动加的,也拦不住——它由引擎隐式维护,只为一个目的:让表级锁能快速判断“这表里有没有行正被锁着”

没有意向锁时,另一个事务想对整张表加

X
锁(比如
ALTER TABLE
或某些显式
LOCK TABLES ... WRITE
),就得逐行检查是否被其他事务持有行锁——大表上这等于拒绝并发。

IS
IX
本身互不冲突,可以并存(多个读/写事务可同时声明意图)
IS
与表级
X
锁冲突 → 表明“有人正在读部分行”,整表写必须等
IX
与表级
S
锁冲突 → 表明“有人正准备写部分行”,整表读也不安全
全表扫描类操作(如没走索引的
UPDATE
)可能触发
IX
+ 行锁升级,但意向锁本身从不阻塞任何操作——真正阻塞的是后续的表锁申请

什么时候你会“感知到”意向锁的存在

你不会直接看到

IS
IX
出现在
SHOW ENGINE INNODB STATUS
的 LOCK WAIT 段里(它只显示最终阻塞链),但你能通过现象反推:

执行
SELECT ... FOR UPDATE
后,另一个会话尝试
LOCK TABLES t1 WRITE
卡住 → 很可能是因为表上有未释放的
IX
锁(即前一个事务还没提交)
事务 A 执行了
SELECT ... LOCK IN SHARE MODE
,事务 B 紧接着执行
ALTER TABLE t1 ADD COLUMN c INT
被挂起 → 是因为
IS
锁和 DDL 需要的
X
表锁冲突
information_schema.INNODB_TRX
里查不到锁类型字段,但
INNODB_LOCK_WAITS
显示等待对象是表名而非行,基本就是意向锁引发的表级等待

常见误判:以为“没加表锁就没事”,其实意向锁早埋伏好了

很多开发者以为自己只操作单行、用了主键条件、没写

LOCK TABLES
,就完全不涉及表级语义——错。只要用了悲观锁语法,意向锁就已生效:

-- session-01
BEGIN;
SELECT * FROM users WHERE id = 100 FOR UPDATE;  -- 自动加 IX 锁(表级)+ X 锁(行级)
-- 此时 users 表已有 IX 锁,哪怕只锁了一行
-- session-02
BEGIN;
LOCK TABLES users WRITE;  -- 立即阻塞:IX 和表级 WRITE 不兼容
即使你用的是唯一索引或主键,只要语句带
FOR UPDATE
LOCK IN SHARE MODE
,意向锁就触发
INSERT
也会触发
IX
锁(尤其涉及自增列时,还叠加自增锁)
显式
LOCK TABLES ... READ/WRITE
是用户层表锁,和意向锁分属不同机制,但会受其约束——这是最容易混淆的一点

调试和验证意向锁行为的实操建议

想确认意向锁是否在起作用?别猜,用这几招直接观察:

performance_schema.data_locks
(MySQL 8.0+):过滤
LOCK_DATA IS NULL AND LOCK_MODE LIKE '%INTENTION%'
,能看到
IS
/
IX
记录
SHOW ENGINE INNODB STATUS\G
TRANSACTIONS
段末尾的
lock struct(s)
,注意
table lock
类型且
lock_mode
IS
IX
测试时务必用两个以上会话,并保持事务未提交——意向锁生命周期=事务生命周期 注意隔离级别影响:在
READ COMMITTED
下,某些非唯一条件查询可能不加间隙锁,但意向锁依然存在

最常被忽略的是:意向锁不是“可选优化”,而是 InnoDB 多粒度锁机制的基础设施。你绕不开它,但可以靠减少长事务、避免无索引扫描、控制悲观锁使用范围来降低它的副作用。

相关推荐