InnoDB 从不主动将行锁升级为全局表锁;所谓“锁表”多因 WHERE 未走索引导致全表扫描并加大量 RECORD 锁,形成“伪表锁”,真表锁仅来自 FLUSH TABLES WITH READ LOCK 等显式命令。

MySQL InnoDB 从不主动把行锁升级为“全局表锁”。所谓“锁表”,99% 是误判——实际是 WHERE 条件没走索引,导致全表扫描,进而对每行(含间隙)都加了 RECORD 锁,锁了上万行后看起来像卡死的表锁。真正的全局表锁只来自 FLUSH TABLES WITH READ LOCK 或备份工具调用,和行锁机制无关。
WHERE 条件没走索引导致“伪表锁”
这是最常见、最容易被当成“锁升级”的场景。InnoDB 的行锁本质是“锁索引项”,没索引就只能扫聚集索引(即整张表),每扫一行就加一个 RECORD 锁 + 间隙锁(Next-key Lock),在 REPEATABLE READ 隔离级别下尤其明显。
-
EXPLAIN显示type: ALL或key: NULL,就是明确信号 - 隐式类型转换:比如
phone VARCHAR(20)字段写WHERE phone = 138,MySQL 自动转成数字比较,索引失效 - 函数包裹:如
WHERE DATE(created_at) = '2024-01-01',索引无法下推 - 前置通配符模糊查询:
WHERE name LIKE '%admin%'必丢索引 - 联合索引顺序错:建了
(city, status),却只查WHERE status = 'active',无法命中
如何确认是不是真表锁
别信 SHOW ENGINE INNODB STATUS\G 里模糊的 “HOLDS THE LOCK(S)” —— 它只报最近事务,漏掉静默持有者。MySQL 8.0+ 唯一可靠方式是查 performance_schema.data_locks:
SELECT LOCK_TYPE, LOCK_MODE, INDEX_NAME FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
LOCK_TYPE = 'TABLE' 才是真表锁(比如 LOCK TABLES t WRITE 或 DDL);LOCK_TYPE = 'RECORD' 就是行锁(哪怕锁了 5 万行)。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
阻塞链看这里:SELECT * FROM performance_schema.data_lock_waits;
避免“伪表锁”的实操要点
核心不是“防止升级”,而是让优化器老老实实走索引、少锁几行。
- 批量操作超 1000 行?拆成
DELETE FROM log WHERE ts 循环执行 - 模糊搜索必须支持任意位置匹配?改用
FULLTEXT索引,或把搜索下沉到 Elasticsearch - 字段有索引但没生效?先
EXPLAIN,再检查是否被函数、OR、隐式转换破坏 - 高频查询条件组合固定?建联合索引覆盖全部
WHERE字段,顺序按选择性从高到低排 - 实在没法加索引又必须大范围更新?临时切到
READ COMMITTED隔离级别——它不用间隙锁,只锁命中的记录(RECORD锁),但要注意幻读风险
真正容易被忽略的是:锁数量爆炸不是锁类型变了,而是你没让 MySQL 找到那条“路”。索引不是可选项,是并发安全的基础设施。一旦发现 data_locks 里 LOCK_TYPE 是 RECORD 却数量异常多,优先翻 EXPLAIN 和字段类型定义,而不是调参数或改隔离级别。

















