需开启log_queries_not_using_indexes=ON,使未走索引的查询强制记入slow log;再结合Rows_examined远大于Rows_sent、EXPLAIN中key为NULL或type为ALL等特征综合判断。

怎么看 slow_log 里哪些语句没走索引
MySQL 慢查询日志本身不直接写「未使用索引」,但会记录 Rows_examined 和 Rows_sent。当 Rows_examined 远大于 Rows_sent(比如 10000:1),基本就是全表扫描——尤其是 WHERE 条件字段没索引时最典型。
开启慢日志时务必加上 log_queries_not_using_indexes = ON,它会让 MySQL 主动把没走索引的查询(哪怕执行很快)也记进 slow log。注意:这会显著增加日志体积,线上建议只在排查期临时打开。
-
long_query_time设为 0 可捕获所有查询,但和log_queries_not_using_indexes同时开时,优先以「是否用索引」为记录依据,不看耗时 - 日志中出现
# Query_time: 0.000123 Lock_time: 0.000011 Rows_sent: 5 Rows_examined: 15234就该盯住这条 - 如果
Rows_examined接近表总行数(可用SELECT COUNT(*) FROM tbl对比),基本坐实全表扫
如何从 slow log 提取真实 SQL 并验证执行计划
slow log 里的 SQL 经过格式化和截断(默认 1024 字符),可能丢掉关键条件或子查询。直接复制日志内容去 EXPLAIN 往往对不上结果。
正确做法是:先用 mysqldumpslow 或正则提取完整语句(推荐用 awk '/^# Time/,/^# User/ {if (/^# Time/ || /^# User/) next; else print}' /var/lib/mysql/slow.log 粗筛),再粘贴到 MySQL 客户端手动补全参数(比如把 WHERE id = ? 改成 WHERE id = 12345),最后跑 EXPLAIN FORMAT=TREE(8.0+)或 EXPLAIN(5.7)。
- 重点看
type列:出现ALL或index(非覆盖索引)就是危险信号 -
key列为NULL表示压根没选上索引 - 如果
possible_keys有值但key是NULL,说明优化器认为走索引还不如扫表——往往因为统计信息不准或条件选择率太高
常见伪“有用索引”陷阱
有些语句看着走了索引,实际效果等同于没建:比如 WHERE status != 'done'、WHERE name LIKE '%abc'、WHERE created_at > '2020-01-01' ORDER BY id LIMIT 10000, 20。这些都可能触发索引失效或回表爆炸。
-
LIKE左模糊('%xxx')一定无法用 B+Tree 索引做范围查找 - 函数操作字段:
WHERE YEAR(create_time) = 2023会让索引失效,应改写为WHERE create_time >= '2023-01-01' AND create_time - 联合索引顺序错位:
INDEX(a,b,c)支持WHERE a=1 AND b=2,但不支持WHERE b=2单独查 -
ORDER BY和WHERE用的不是同一组索引字段时,容易触发 filesort,尤其配合大LIMIT offset, size时Rows_examined会飙升
线上环境别只盯着 slow log
很多没走索引的语句根本进不了 slow log——因为它们执行太快(long_query_time 没踩线),但调用量极大,积少成多拖垮数据库。这类语句在 performance_schema.events_statements_summary_by_digest 里更早暴露:看 SUM_ROWS_EXAMINED / SUM_ROWS_SENT 比值,再结合 COUNT_STAR 排序,能揪出高频低效语句。
另外,information_schema.PROCESSLIST 里如果频繁看到 Copying to tmp table 或 Creating sort index 状态,也是索引缺失的强提示,比等 slow log 更及时。


















