is锁和ix锁是意向锁,不直接锁数据行,而是声明事务将对表中某些行加s锁或x锁;ix锁在事务首条修改语句时即加,持续至事务结束,阻塞ddl等需要表级独占权限的操作。

IS锁和IX锁到底在锁什么
意向锁(Intention Shared / Intention Exclusive)不是直接锁数据行,而是告诉MySQL:“我接下来要对这张表里的某些行加S锁或X锁”。它本身不阻塞读写,但会阻止其他事务对整张表执行DROP TABLE、ALTER TABLE这类需要表级独占权限的操作。
常见错误现象:ERROR 1205 (40001): Deadlock found when trying to get lock 或长时间卡在Waiting for table metadata lock,往往是因为一个事务持有了IX锁(比如正在执行UPDATE),另一个事务却想对同一张表做TRUNCATE——后者需要SCH-M(schema-modify)锁,而IX与SCH-M互斥。
- IS锁:表示事务将要对表中某些行加S锁(如
SELECT ... LOCK IN SHARE MODE) - IX锁:表示事务将要对表中某些行加X锁(如
UPDATE、DELETE、INSERT) - IS和IX之间兼容,可以共存;但IS/IX都与
SCH-M、X(表级排他锁)不兼容
怎么查当前表上有没有IS/IX锁
意向锁不会出现在information_schema.INNODB_TRX里,得看INNODB_LOCKS(MySQL 5.7)或performance_schema.data_locks(8.0+)。但更实用的是结合SHOW ENGINE INNODB STATUS\G,在TRANSACTIONS部分找lock_mode IX或lock_mode IS字样。
使用场景:线上出现慢查询堆积,怀疑是元数据锁争用,优先检查是否有长事务没提交,导致它持有的IX锁一直不释放,堵住了DDL。
- MySQL 8.0+ 推荐查:
SELECT * FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'table_name'; - 注意
LOCK_MODE字段值为IS或IX,且LOCK_DATA为NULL——这说明是表级意向锁,不是行锁 - 不要依赖
INNODB_LOCK_WAITS,它只记录已发生的死锁等待,不反映当前持有的意向锁
为什么加了WHERE却还是上了IX锁
哪怕UPDATE t SET a=1 WHERE id = 123只命中一行,InnoDB仍会对整张表加IX锁。这不是冗余设计,而是为了快速判断“这张表是否正被并发修改”——如果每次都要扫描所有行锁才能确认,性能就垮了。
性能影响:IX锁本身开销极小,但它是后续所有表级操作的拦路虎。比如一个大事务执行了UPDATE后迟迟不提交,此时另一个DBA想给表加索引,就会被卡住,且这个等待不会体现在慢日志里。
- IX锁在事务开始第一条修改语句时就加上,直到事务结束(COMMIT/ROLLBACK)才释放
- 即使
UPDATE语句因WHERE条件没匹配到任何行,IX锁依然会上 - 显式开启事务(
BEGIN)后只执行SELECT,不会触发IS锁;但一旦执行过UPDATE,后续哪怕全是SELECT,IX锁也还在
如何避免IX锁干扰DDL操作
核心思路不是消灭IX锁(它没法也不该被禁用),而是缩短它存在的时间窗口。重点盯住那些“开了事务却不提交”的代码逻辑。
容易踩的坑:ORM框架里用了transaction.atomic(Django)或@Transactional(Spring),但内部调用了第三方HTTP请求或文件IO,结果网络超时导致事务卡住;或者Python里用try/except吞掉了异常,忘了rollback。
- 应用层设置事务超时:
SET innodb_lock_wait_timeout = 10;(会中断等待,但不释放已持锁) - 数据库侧监控:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60;找出运行超1分钟的事务 - 禁止在事务里做非数据库操作;必须做的,拆成“先DB后外部”,或改用最终一致性
真正难处理的是隐式事务:autocommit=0时,哪怕只执行一条UPDATE,也会开启事务并持有IX锁,直到显式commit。这点连很多DBA都会忽略。











