索引失效导致慢查询日志记录 Rows_examined 过大怎么排查

文章导读
当慢查询日志中 Rows_examined 远大于 Rows_sent 或接近表总行数时,通常意味着索引失效导致全表扫描。排查需先确认慢日志配置生效,再结合 EXPLAIN 分析执行计划中的 type 和 key 字段,最后修正 SQL 写法或补充索引。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
  7. 参考来源
A A

当慢查询日志中 Rows_examined 远大于 Rows_sent 或接近表总行数时,通常意味着索引失效导致全表扫描。排查需先确认慢日志配置生效,再结合 EXPLAIN 分析执行计划中的 type 和 key 字段,最后修正 SQL 写法或补充索引。

先说结论:Rows_examined 过大本质是扫描行数过多,核心原因是索引未命中或失效。

  • 先定位:对比 Rows_examined 与表总行数,确认是否接近 80% 以上。
  • 先做:使用 EXPLAIN 检查 type 是否为 ALL 及 key 是否为 NULL。
  • 再验证:优化后观察 Rows_examined 是否显著下降且 key 字段有值。

命令速用版

以下命令用于快速确认日志状态、表规模及执行计划,直接在 MySQL 客户端执行:

SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SELECT table_rows FROM information_schema.tables WHERE table_name = 'your_table';
EXPLAIN SELECT * FROM your_table WHERE condition;

为什么会这样

索引失效导致 MySQL 无法利用 B+ 树快速定位,只能逐行扫描全表数据。常见于字段函数操作、隐式类型转换、模糊匹配前缀通配符等场景,导致 Rows_examined 数值接近表实际行数。

慢查询日志记录的是执行时间超过阈值的语句,若 Rows_examined 过大但执行时间短,可能因表较小未入日志;若入日志且 Rows_examined 高,说明扫描成本高。索引失效时,优化器无法使用索引列进行范围查找,转而执行全表扫描,直接推高扫描行数。

分步处理

第一步:确认慢查询日志配置生效

执行 SHOW VARIABLES 检查 slow_query_log 是否为 ON,long_query_time 是否合理(生产建议设为 1 秒或 0.5 秒)。确认 log_output 为 FILE,避免使用 TABLE 模式导致查询慢日志本身变慢。检查 slow_query_log_file 路径权限,确保 MySQL 进程有写权限。

第二步:提取高 Rows_examined 的 SQL

解析慢日志文件,重点关注 Rows_examined 值。若该值大于表行数的 80%,基本可判定为全表扫描。可使用 pt-query-digest 工具分析日志,筛选 Rows_examined/Rows_sent 比值高的语句。

第三步:结合 EXPLAIN 验证索引状态

索引失效导致慢查询日志记录 Rows_examined 过大怎么排查

对疑似 SQL 执行 EXPLAIN 命令。检查 type 字段,若为 ALL 则表示全表扫描;检查 key 字段,若为 NULL 则表示未使用索引。查看 Extra 字段,若含 Using filesort、Using temporary 或 Full scan on NULL key,需重点优化。

第四步:修正 SQL 或调整索引

若因字段使用函数导致失效(如 YEAR(create_time)),改为范围查询(create_time >= '...' AND create_time < '...')。若因隐式类型转换(如 VARCHAR 字段查数字),确保查询参数类型一致。若因模糊匹配(LIKE '%val'),考虑反转字段存储或使用搜索引擎。

怎么验证是否生效

优化后再次执行 EXPLAIN,确认 type 不再是 ALL,key 显示具体索引名。在生产环境观察慢日志,确认该 SQL 不再频繁出现或 Rows_examined 显著降低。若开启了 log_queries_not_using_indexes,确认优化后不再记录该 SQL 的无索引警告。

常见坑

小表全表扫描可能因执行时间短(如小于 10ms)而不进入慢日志,需定期筛查大表常用查询。log_queries_not_using_indexes 对 INSERT SELECT 或子查询中的扫描无效,需单独分析。OR 条件中包含非索引字段会导致整个查询退化为全表扫描,即使其他字段有索引。

常见问题

为什么 EXPLAIN 显示 type=all 却没进慢日志?

慢日志只记录执行时间超过 long_query_time 的语句,小表全表扫描可能耗时极短而不被记录。

Rows_examined 高一定代表没索引吗?

不一定,也可能是索引失效,如对字段做函数操作、隐式类型转换导致优化器放弃索引。

如何捕获未走索引但未超时的 SQL?

开启 log_queries_not_using_indexes 参数,但需注意该参数对部分复杂查询无效且可能增加磁盘 IO 压力。

参考来源

  • mysql 查询执行缓慢如何排查_深入分析 mysql 慢查询日志定位
  • 怎样在 MySQL 中通过慢日志排查全表扫描的隐患 SQL?
  • mysql 慢查询日志如何分析_mysql 日志优化方法
  • 面试被问 MySQL 慢 SQL 怎么排查?看完这篇直接给面试官讲明白-CSDN 博客
  • MySQL 慢查询排查完整流程
  • MySQL 慢查询日志分析实战:定位并优化慢 SQL(彻底解决数据库卡顿)
  • MySQL 生产查询慢,该如何排查
  • MySQL 性能优化之慢查询的排查与解决方案