handler_read_next高不等于范围扫描合理,需结合explain的rows与返回行数判断过滤效率;order by不匹配或复合索引顺序不当会引发冗余遍历;测试前须flush status以获取准确增量。

Handler_read_next 高 ≠ 范围扫描一定合理
Handler_read_next 统计的是“按索引顺序读下一行”的次数,常见于范围查询(WHERE col BETWEEN a AND b)、ORDER BY 索引列、或覆盖索引扫描。但它本身不区分“有效扫描”和“无效遍历”——比如你查 WHERE status IN ('paid', 'shipped'),而 status 上只有单列索引,MySQL 可能用索引定位到第一个 'paid',然后一路 read_next 扫到末尾才切到 'shipped',中间大量跳过非目标行,Handler_read_next 就会虚高。
关键判断点不是数值绝对大小,而是结合 EXPLAIN 的 rows 和实际返回行数:如果 rows=1000 但只返回 5 行,而 Handler_read_next ≈ 1000,说明索引过滤性差,扫描冗余严重。
ORDER BY 索引不匹配导致 read_next 暴涨
当 ORDER BY 字段不在查询条件索引的后缀位置时,MySQL 常放弃索引排序,改用 filesort;但如果优化器误判或强制走某个索引,就可能出现“先用索引扫出一堆数据,再内存排序”,此时 Handler_read_next 会远高于 LIMIT 行数。
- 例如
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC,若只有(user_id)索引,无(user_id, created_at),MySQL 可能先用user_id索引扫出全部该用户订单(触发大量Handler_read_next),再排序 - 加
FORCE INDEX (idx_user_created)后若Handler_read_next显著下降且接近 LIMIT 值,说明原执行路径低效 -
EXPLAIN中出现Using filesort时,Handler_read_next高大概率是副作用,不是主因
复合索引顺序错误引发隐式全索引扫描
复合索引列顺序决定其可支持的扫描模式。Handler_read_next 异常高,往往意味着 MySQL 在索引内部做了长距离顺序遍历,而不是精准跳转。
典型场景:
- 索引建为
(a, b, c),但查询是WHERE b = ? AND c = ?:无法利用索引前导列,可能退化为全索引扫描(Handler_read_first = 1+ 大量Handler_read_next) - 查询含
WHERE a = ? AND c = ?(跳过b):a可用,但c无法走索引,仍需在a对应范围内逐行read_next过滤 -
ORDER BY b, c但索引是(a, b, c):a无约束时,无法按b,c顺序高效扫描,Handler_read_next会覆盖整个索引片段
容易被忽略的干扰项:Session 级统计与 FLUSH STATUS 时机
Handler_read_next 是 session 级累计值,不自动清零。如果你在同一个连接里连续跑多个查询,没手动 FLUSH STATUS,数值就是叠加的——看着高,其实只是“上一条慢查询拖累的余量”。
实操建议:
- 每次测单条 SQL 前必须执行
FLUSH STATUS,否则数据不可比 - 避免复用长连接做多次测试,不同连接间状态隔离更干净
-
SHOW SESSION STATUS LIKE 'Handler_read%'比GLOBAL更准,后者混入后台线程、系统操作噪音 - 注意对比的是增量,不是绝对值:比如
Handler_read_next从 1000 → 1050,实际只多了 50 次,而非总值 1050
真正难判断的,是 Handler_read_next 高的同时 Handler_read_key 也高——这说明索引在用,但路径不够紧凑。这时候得回看索引设计是否贴合查询模式,而不是急着加索引或改 SQL。











