如何排查MySQL中由于内联子查询导致的非预期事务锁定?

雨墨吖_3157

雨墨吖_3157

2026-07-25

980人浏览

原创

内联子查询会意外加锁,因其在innodb中被视为dml上下文,对扫描到的所有候选行加锁(如x锁或间隙锁),而非仅最终匹配行;常见诱因包括非唯一索引、范围扫描、未走索引、order by + limit及相关子查询等。

如何排查mysql中由于内联子查询导致的非预期事务锁定?

内联子查询为什么会意外加锁?

内联子查询(比如 UPDATE ... WHERE id IN (SELECT ...) 或 DELETE ... WHERE x = (SELECT ...))在 MySQL 中不是“只读取”,它可能被 InnoDB 视为需要加锁的 DML 上下文。尤其当子查询走的是非唯一索引、范围扫描,或未命中索引时,InnoDB 会为扫描到的**所有候选行**加锁(lock_mode X locks rec but not gap 或更糟的 gap before rec),而不仅限于最终匹配的那几行。

常见诱因包括:子查询里用了 ORDER BY + LIMIT(但外层没加 FOR UPDATE)、子查询条件未覆盖索引最左前缀、子查询引用了被更新表本身(即相关子查询),这些都会触发更宽泛的锁范围。

怎么确认是内联子查询惹的祸?

别急着改 SQL —— 先定位。核心动作是抓取死锁或锁等待发生时的真实执行路径:

  • 运行 SHOW ENGINE INNODB STATUS\G,重点看 LATEST DETECTED DEADLOCK 里两个事务的 SQL,检查是否一方含 IN (SELECT ...) 或 = (SELECT ...) 结构
  • 查 information_schema.INNODB_TRX,过滤 trx_state = 'LOCK WAIT' 的事务,用 trx_query 字段确认是否正在执行带子查询的 UPDATE/DELETE
  • 对可疑 SQL 手动执行 EXPLAIN FORMAT=TRADITIONAL,注意 type 是否为 ALL 或 index,key 是否为 NULL,Extra 是否含 Using temporary; Using filesort —— 这些都暗示锁范围失控

MySQL 8.0+ 怎么查子查询实际锁了哪些行?

MySQL 5.7 的 INNODB_LOCKS 表在 8.0+ 已废弃,必须转向 performance_schema.data_locks。关键点在于:子查询产生的锁不会单独标记“这是子查询的锁”,而是和主语句一起出现在同一事务的锁记录中。

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载

执行以下查询,能直观看到当前事务持有的所有锁(含子查询引入的):

SELECT
  ENGINE_TRANSACTION_ID,
  OBJECT_SCHEMA,
  OBJECT_NAME,
  INDEX_NAME,
  LOCK_TYPE,
  LOCK_MODE,
  LOCK_DATA
FROM performance_schema.data_locks
WHERE ENGINE_TRANSACTION_ID IN (
  SELECT trx_id FROM information_schema.INNODB_TRX
  WHERE trx_query LIKE '%IN (SELECT%' OR trx_query LIKE '%= (SELECT%'
);

如果 LOCK_DATA 返回大量非预期主键值(比如你要删 id=100,结果锁了 id 从 50 到 150 的整段),基本可断定子查询扫描范围过大。

如何避免内联子查询引发的锁扩散?

根本解法不是禁用子查询,而是控制其执行计划和生命周期:

  • 把内联子查询拆成两步:先 SELECT ... INTO @var 获取 ID 列表,再用 WHERE id IN (@var1, @var2, ...) —— 注意 @var 不能存多值,需改用临时表或应用层拼接
  • 确保子查询 **强制走索引**:给子查询 WHERE 条件字段建联合索引,且满足最左前缀;避免在子查询里用函数(如 DATE(created_at))或隐式类型转换
  • 若子查询结果集小(JOIN 替代 IN,例如 UPDATE t1 JOIN t2 ON t1.id = t2.ref_id SET ... —— JOIN 在大多数情况下锁范围更精准
  • 在事务里执行带子查询的 DML 前,显式加 SELECT ... FOR UPDATE 锁住子查询结果,避免后续 UPDATE 重复扫描加锁

最容易被忽略的是:相关子查询(子查询里引用外层表字段)在 RR 隔离级别下会触发间隙锁,哪怕外层只更新一行,子查询扫描的整个范围都可能被锁死。这种场景下,优先考虑业务逻辑重构,而非调参。

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

2133

6

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.21

1299

5

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

755

5

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.19

2872

5

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.25

4788

4

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.08

1099

3

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.11

5051

4

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

2023.08.14

4482

7

mysql事务隔离级别
mysql事务隔离级别

mysql规范中定义了四种事务隔离级别,不同的隔离级别对事务的处理有所不同。本专题为大家提供mysql事务隔离级别相关的文章内容,大家可以免费体验。

2023.08.16

5874

11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 181人学习

使用phpenv集成环境安装极致CMS
使用phpenv集成环境安装极致CMS

共2课时 | 289人学习