explain看到type=all就说明没走索引,mysql 8.0.19+支持直接explain update,需重点检查type(非all)、key(非null)、rows(远小于总数),type=all意味着全表扫描并逐行加锁。

EXPLAIN 看到 type=ALL 就说明没走索引
别猜,直接跑 EXPLAIN UPDATE —— MySQL 8.0.19+ 支持对 UPDATE 语句直接 EXPLAIN,这是唯一靠谱的判断方式。重点盯三列:type(不能是 ALL)、key(不能是 NULL)、rows(应远小于表总行数)。type=ALL 意味着全表扫描,InnoDB 会沿着主键逐行加锁,锁范围爆炸式扩大。
常见假象:
- WHERE order_no = 'O123' 看着像走索引,但 order_no 是 VARCHAR,传入的是数字 123 → 触发隐式转换,索引失效
- WHERE DATE(create_time) = '2026-07-01' → 函数包裹字段,索引完全失效
- WHERE status = 'CANCELED' → status 区分度极低(比如只有 3–5 个值),优化器可能直接放弃索引,选全表扫
索引建了但 UPDATE 还不走?检查这三件事
建索引 ≠ 自动生效。必须验证执行计划是否真用了它:
- 用
SHOW INDEX FROM table_name确认索引存在,且Seq_in_index和Cardinality合理(低基数列如is_deleted不适合单独立索引) - 复合索引字段顺序必须匹配 WHERE 条件:若查
WHERE category = ? AND created_at > ?,索引得是(category, created_at),反过来就大概率不走 - 统计信息过期会导致优化器误判,执行
ANALYZE TABLE table_name强制刷新,再跑一遍EXPLAIN
UPDATE 语句里藏着“隐形索引杀手”
有些写法看着正常,实则让索引彻底失效:
-
WHERE field != 'X'或WHERE field IS NOT NULL:大多数情况下不走索引(尤其是 B-tree 索引) -
WHERE field LIKE '%abc':前导通配符,无法利用索引的有序性 -
WHERE JSON_EXTRACT(data, '$.status') = 'active':JSON 字段上没生成虚拟列 + 索引,函数调用必全表扫 - 多表 JOIN 的 UPDATE 中,任意一张表的关联条件没走索引,整个语句可能退化为嵌套循环 + 全表扫描
sql_safe_updates=1 会强制要求 WHERE 必须走索引
这个开关打开时,MySQL 会拒绝执行任何没走索引的 UPDATE/DELETE。现象是报错:You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column。这不是性能问题,是安全拦截。
排查路径:
- 查当前值:SELECT @@sql_safe_updates
- 临时关闭(仅调试):SET sql_safe_updates = 0
- 但真正要解决的是让 WHERE 条件回归索引路径,而不是关开关——否则上线后一模一样的 SQL 在生产环境仍会失败
锁不是慢的原因,是没走索引的后果;而没走索引,往往藏在你写完就提交的那行 WHERE 条件里。











