rows_examined远大于rows_sent是索引未命中或失效的明确信号,表明innodb在做大量无效行扫描,消耗io、cpu和buffer pool;常见原因包括like '%xxx'、函数包裹索引列、隐式类型转换及filesort/临时表。

Rows_examined 远大于 Rows_sent 就是索引没用好
这不是“可能有问题”,而是存储引擎确实在做大量无用功:InnoDB 逐行读取、解压、判断条件是否满足,每一行都消耗 buffer pool、磁盘 IO 和 CPU cycles。只要 Rows_examined 比 Rows_sent 高出一个数量级(比如 50000:1),基本可以断定 WHERE 条件没走对索引,或者根本没走索引。
- 常见直接诱因:
WHERE条件用了LIKE '%abc'、YEAR(create_time) = 2025、字符串字段和数字值比较(如status = 1但status是VARCHAR) -
ORDER BY或GROUP BY也会推高Rows_examined,哪怕WHERE很快——因为触发了Using filesort或临时表遍历 - 注意区分设计使然 vs 真实低效:
LIMIT 9990,10天然扫描多行,但WHERE id = ?却扫了 5 万行,就是典型索引失效
怎么从慢日志里快速揪出这类查询
别手动翻日志。用 mysqldumpslow 按扫描量排序最有效:
mysqldumpslow -s ar -t 10 /var/log/mysql/slow.log
其中 -s ar 表示按 Rows_examined 的平均值(a = average, r = Rows_examined)排序;-t 10 取前 10 条。输出里会明确列出每条的 Rows_examined 和 Rows_sent。
如果日志是纯文本格式,也可以用 awk 快速筛出比值 > 100 的条目:
awk '/Rows_examined:/ && /Rows_sent:/ {r=$3; s=$6; if(r/s > 100) print}' /var/log/mysql/slow.log | head -20
注意前提:MySQL ≥ 5.7(Rows_examined 默认开启);若为 5.6,需设 long_query_time = 0 才能捕获所有高扫描语句。
确认是不是真由引擎行为导致 —— 看 EXPLAIN 的 rows 列
EXPLAIN 输出里的 rows 字段,就是优化器预估的 Rows_examined 值。它和慢日志里的实际值通常接近(尤其在数据分布稳定时)。如果 EXPLAIN 显示 rows = 100000,而实际 Rows_examined 也是这个量级,说明问题不在执行时的数据突变,而在 SQL 写法或索引结构本身。
- 检查
type是否为ALL(全表扫描)或index(全索引扫描) - 看
key是否为NULL,或用了非预期的索引 -
Extra出现Using where; Using index condition是好的;出现Using filesort或Using temporary就要警惕
容易被忽略的点:Rows_examined 高 ≠ SQL 写得烂
有时候 Rows_examined 突然飙升,不是 SQL 改了,而是数据变了。比如某天突然插入大量 status = 'default' 的记录,导致该字段索引选择性暴跌,优化器干脆放弃走索引,转而全表扫描——这时 EXPLAIN 可能和昨天一模一样,但实际扫描行数翻了十倍。
还有种情况:应用层做了分页缓存,但缓存穿透后一次性拉取几十万行再内存过滤,SQL 看似简单(SELECT * FROM t WHERE deleted = 0),Rows_examined 却极高。这种问题光看 SQL 和索引没用,得结合业务调用链一起看。











