type=all表示mysql正在执行全表扫描,即未使用任何索引、逐行读取整张表;常见原因包括无索引、索引列使用函数、隐式类型转换、违反最左前缀原则或优化器误判。
“全表扫描”就是mysql正在逐行读取整张表,没走任何索引——这不是偶发卡顿,而是每次执行都稳定地慢。
看到 type=ALL 就该立刻警觉
在Navicat执行计划里,type 列显示为 ALL,代表全表扫描。它和 index(Full Index Scan)有本质区别:ALL 是直接从数据页随机读取整张表,I/O开销大、CPU利用率高;而 index 至少还在索引树上顺序遍历叶子节点。
常见触发场景包括:
-
WHERE条件中对索引字段用了函数,比如YEAR(create_time) = 2023 - 查询只用了复合索引的非最左列,例如索引是
(status, user_id),但写了WHERE user_id = 123 - 字段类型不匹配:数据库里是
VARCHAR,SQL里却传了数字123,触发隐式转换 -
possible_keys有值但key为空——说明有索引可用,但优化器弃用了
key 为空但 possible_keys 有值,到底该信谁
possible_keys 是优化器“觉得可能用得上的索引”,key 才是它“实际选中的索引”。两者不一致时,别急着建新索引,先查原因:
- 执行
ANALYZE TABLE your_table;更新统计信息——旧统计可能导致误判 - 检查
WHERE是否含IS NULL、!=、LIKE '%abc'这类基本无法走B+树索引的写法 - 确认是否对索引字段做了表达式计算,比如
WHERE col1 + col2 > 100或JSON_EXTRACT(data, '$.name') = 'Alice' - 用
EXPLAIN FORMAT=JSON查看filtered值:低于 10% 说明该条件筛选性太差,建索引收益极低
Navicat 的 “Missing Index” 建议不能直接照搬
Navicat 17 右键执行计划 → “Missing Index Details” 给出的建议,只是基于当前SQL的字段组合做的启发式推测。它不会告诉你:
- 这个组合是否已作为最左前缀存在于某个现有索引中(比如已有
(a, b, c),它仍可能建议(a, b)) - 字段顺序是否合理:查询是
WHERE tenant_id = ? AND create_time > ? ORDER BY updated_at DESC,正确索引应为(tenant_id, create_time, updated_at);把create_time放第一位就失效 - 该字段是否适合建索引:对
TEXT、超长VARCHAR或JSON字段建普通B-tree索引,MySQL会截断,实际不可用
rows 值远大于实际返回行数,说明索引没对上路子
rows 是MySQL预估要扫描的行数,不是结果集大小。如果 rows 接近表总行数(比如表有 50 万行,rows 显示 48 万),哪怕 key 显示用了某个索引,也大概率是假命中——可能只利用了索引的最左一列做范围扫描,后续条件仍在回表后过滤。
真正有效的索引,应该让 rows 和实际返回行数相差不超过一个数量级。否则就得重新审视:
- WHERE 条件是否能全部被复合索引覆盖(最左前缀原则)
- ORDER BY 字段是否已包含在索引末尾,避免
Using filesort - SELECT 的字段是否都在索引中,避免回表(即“覆盖索引”)
索引不是越多越好,关键是让 key 列真正填上值,且 rows 接近真实筛选结果——这点最容易被忽略,也是调优成败的分水岭。











