Rows_examined远大于Rows_sent说明查询扫描行数远超返回行数,主因是索引未生效,如WHERE条件用函数、隐式类型转换、联合索引顺序不匹配等;需结合EXPLAIN验证执行计划并优化索引。

Rows_examined 为什么远大于 Rows_sent?
这是慢查询日志里最常触发警觉的信号:比如 Rows_examined: 248932,但 Rows_sent: 12。它不意味着“查了24万行只返回12行就一定错”,而是说明优化器在执行路径中扫描了大量行,才筛出最终结果。
常见原因包括:
- WHERE 条件未命中有效索引,导致全表或大范围索引扫描
- 使用了函数或表达式包裹索引列(如
WHERE YEAR(created_at) = 2024),使索引失效 - 联合索引顺序不匹配查询条件(如索引是
(a,b,c),但只查WHERE c = ?) - 隐式类型转换(如字段是
VARCHAR,但传入数字参数WHERE user_id = 123)
注意:Rows_examined 统计的是 server 层扫描的行数,不含存储引擎内部跳过的行(例如 InnoDB 的 MVCC 版本链遍历),所以它比实际物理 I/O 更“轻”,但仍足够反映逻辑扫描开销。
如何验证 Rows_examined 是否真实反映执行开销?
不能只信慢日志里的数字——它只是预估值或执行后统计值,且受 min_examined_row_limit 过滤影响。真正可靠的方式是复现 + EXPLAIN 对照:
- 从慢日志中提取完整 SQL(注意还原注释、参数占位符)
- 在相同环境下执行
EXPLAIN FORMAT=TRADITIONAL,重点关注rows列(5.7+ 对应Rows_examined预估)和key列(是否用了预期索引) - 若
EXPLAIN的rows明显小于慢日志中的Rows_examined,可能是语句执行时数据分布变化(如统计信息过期),需运行ANALYZE TABLE - 若两者接近但
Rows_sent极小,大概率是LIMIT被下推失败,或排序/分组前已扫描大量行
特别提醒:EXPLAIN 不会执行语句,也不统计锁等待;而慢日志的 Rows_examined 是真实执行后的结果,含所有 runtime 行扫描行为(包括临时表、派生表、子查询展开等)。
Rows_sent 为 0 但 Rows_examined 很高,意味着什么?
典型场景是 INSERT ... SELECT、UPDATE ... WHERE 或 DELETE ... WHERE 类语句。此时 Rows_sent 恒为 0(不返回结果集),但 Rows_examined 照样统计扫描行数——它仍是判断 WHERE 效率的核心指标。
例如这条日志:
# Query_time: 3.214567 Lock_time: 0.000123 # Rows_sent: 0 Rows_examined: 1843200 SET timestamp=1749405600; UPDATE orders SET status = 'shipped' WHERE created_at < '2025-01-01';
说明该更新语句扫描了 184 万行才找到匹配记录。即使没返回数据,它依然可能拖慢整个实例——尤其是加了行锁之后。
这种情况下优化方向很明确:给 created_at 加索引,或改用分区裁剪(如按月分区)。别被 Rows_sent: 0 欺骗,以为“没输出就不耗资源”。
min_examined_row_limit 如何悄悄过滤掉你关心的问题?
这个参数默认是 0,但一旦被设为非零值(比如 SET GLOBAL min_examined_row_limit = 1000),就会导致“扫描少于 1000 行的慢查询不进日志”——哪怕它的 Query_time 超过了 long_query_time。
后果很隐蔽:
- 你看到的慢日志全是“大扫描”,却漏掉了大量
Query_time长但Rows_examined小的语句(比如高并发下的简单主键查询因锁争用变慢) -
log_queries_not_using_indexes也会受此限制:即使没走索引,只要扫描行数低于阈值,照样不记录 - 线上排查时容易误判“没有低效查询”,其实是被参数拦在门外了
建议生产环境始终设为 0,靠 long_query_time 和 log_queries_not_using_indexes 控制粒度,而不是用 min_examined_row_limit 做二次过滤——它的存在意义主要是降低日志体积,不是辅助诊断。


















