extra列出现using where且type为all,或rows接近表总行数时,表明存在全表扫描风险;需结合analyze table、避免隐式转换、检查最左前缀等手段优化。

怎么看 Extra 里有没有全表扫描警告
MySQL 的 EXPLAIN 输出中,Extra 列是判断索引是否失效的关键。真正危险的信号不是“没走索引”,而是出现 Using where; Using index 这类看似正常、实则暗藏隐患的组合——尤其当 type 是 ALL 或 index 时,说明 MySQL 正在扫描整张表或整个索引树。
重点关注以下几种 Extra 值:
-
Using filesort:不直接导致锁全表,但常伴随type=ALL,说明排序无法利用索引,可能触发临时表+全扫描 -
Using temporary:几乎必然伴随全表扫描(尤其在GROUP BY或子查询中),且会加更多锁 - 空值或只有
Using where(且type=ALL):这是最典型的“索引失效+全表扫描”标志 -
Impossible WHERE虽然不扫表,但说明条件写错,容易被误认为“没数据所以快”,实则掩盖了逻辑缺陷
为什么 type=range 也可能锁全表
很多人以为只要 type 不是 ALL 就安全,但实际并非如此。比如对主键范围查询:WHERE id BETWEEN 1 AND 1000000,type 是 range,但如果该区间覆盖了绝大部分数据,InnoDB 仍可能放弃索引、退化为聚簇索引全扫描——此时 Extra 可能为空或仅含 Using where,但 rows 显示预估扫描行数接近表总行数。
更隐蔽的是二级索引 + ORDER BY 场景:
SELECT * FROM orders WHERE status = 'pending' ORDER BY create_time;
即使 status 有索引,若 create_time 未包含在联合索引中,MySQL 可能选择走 status 索引取出所有 pending 记录,再回表排序——这时 Extra 显示 Using index condition; Using filesort,rows 是 status 匹配的全部行数,等价于锁住这些记录对应的所有聚簇索引页。
key 显示用了索引,但 rows 大得离谱怎么办
key 非空只代表“选了这个索引”,不代表“用得好”。关键看 rows 和实际业务数据分布的匹配度:
- 执行
SELECT COUNT(*) FROM t WHERE xxx对比EXPLAIN中的rows,误差超过 3 倍就要怀疑统计信息过期 - 运行
ANALYZE TABLE t更新统计信息,再看EXPLAIN是否变化 - 检查是否用了函数或隐式类型转换,例如
WHERE DATE(create_time) = '2024-01-01'或WHERE mobile = 13800138000(mobile 是字符串类型)——这类写法会让key显示有索引,但实际无法使用索引下推,rows会爆涨 - 确认查询条件是否符合最左前缀原则,比如联合索引
(a,b,c),只用WHERE b = ?是无法命中索引的,key可能为空或显示NULL
如何验证是否真锁了全表(而不仅是扫描)
EXPLAIN 只反映执行计划,不体现锁行为。要确认是否锁全表,得结合 INFORMATION_SCHEMA.INNODB_TRX 和 INNODB_LOCK_WAITS 查实时锁状态:
SELECT trx_id, trx_state, trx_query FROM INFORMATION_SCHEMA.INNODB_TRX;
如果看到长事务执行了 UPDATE ... WHERE 语句,且 trx_state = 'RUNNING',再查它的 trx_rows_locked 字段(需开启 innodb_status_output_locks)——值远大于实际影响行数,大概率是索引失效导致锁升级。
更直接的方式是复现时开两个会话:
- 会话 A 执行疑似问题 SQL(如
UPDATE t SET x=1 WHERE y LIKE '%abc%') - 会话 B 立即尝试
SELECT * FROM t WHERE id = 1 FOR UPDATE,如果卡住,说明 A 已经锁住了大量记录甚至整个聚簇索引
这种锁扩散往往不是因为 SQL 写错,而是优化器基于过时统计信息选择了错误索引路径,或者谓词条件本身无法有效过滤——这时候改写 SQL 或强制指定索引(USE INDEX)比等自动优化更可靠。











