Handler_read_key高说明索引被真正用于精准定位行,Handler_read_rnd_next高则表明存在全表扫描或回表开销大;需配合FLUSH STATUS与单次查询观测,二者数量级关系比EXPLAIN更真实反映索引使用效果。

Handler_read_key 高不高,直接说明索引有没有被真正用上
这个值代表 MySQL 通过索引键(比如 WHERE user_id = ?)精准定位行的次数。它不是“用了索引就加1”,而是“靠索引跳转到某行”才计数——所以只要看到 Handler_read_key 显著上升,基本可以确认索引参与了定位。
常见误区:SELECT * 扫全表但走了主键索引,Handler_read_key 可能为 0;而一个带 WHERE status = 'active' 的查询如果命中了 status 索引,Handler_read_key 就会明显增加。
- 线上观察时,建议在业务低峰期先执行
FLUSH STATUS,再跑目标 SQL,立刻查SHOW STATUS LIKE 'Handler_read_key' - 如果该值始终接近 0,即使
EXPLAIN显示type=ref或key=xxx,也要怀疑是否条件没打中索引最左前缀,或字段存在隐式类型转换 - 对比多个相似查询时,
Handler_read_key增幅大的那个,往往更依赖索引、也更可控
Handler_read_rnd_next 高得离谱,基本等于在裸奔扫表
Handler_read_rnd_next 是 InnoDB 层面最刺眼的性能红灯:它统计的是“在数据文件里一行行往下读”的请求数,本质就是全表扫描或临时结果集回表后的顺序读。只要它远高于 Handler_read_key 或 Handler_read_next,就说明索引没拦住大部分数据访问。
典型场景:SELECT name, email FROM users WHERE city = 'Shanghai',但 city 列没索引,或者有索引但 city 值重复率太高(比如 80% 用户都在上海),优化器干脆放弃走索引,直接扫聚簇索引——这时 Handler_read_rnd_next 会飙升,而 Handler_read_key 几乎不动。
- 注意区分
Handler_read_rnd和Handler_read_rnd_next:_rnd多见于排序临时表的随机取行,_rnd_next才是真正的“扫表级”指标 - 如果某条慢查询的
Handler_read_rnd_next达到几万甚至几十万,别急着调优 SQL,先检查对应列是否缺失索引、或现有索引是否覆盖了WHERE条件 - 建复合索引时,如果
WHERE用a = ? AND b > ?,但SELECT还要返回c,记得把c加进索引末尾做成覆盖索引,否则仍会触发大量Handler_read_rnd_next
Handler_read_next 和 Handler_read_first 揭示索引扫描模式
Handler_read_next 上升,说明 MySQL 正在按索引顺序“向后扫”——常见于范围查询(WHERE create_time BETWEEN ? AND ?)、ORDER BY indexed_col 或联合索引的最左前缀匹配后继续遍历。它本身不坏,但值过大可能意味着范围太宽、或缺少更精确的过滤条件。
Handler_read_first 高,则大概率是在做全索引扫描(比如 SELECT id FROM t ORDER BY id 没加 LIMIT),它每次从索引最左端开始读,对性能压力不小,尤其当索引很大时。
- 如果
Handler_read_first高而Handler_read_key很低,基本可断定是“伪索引使用”:SQL 没带有效WHERE,只是靠索引排序或遍历,实际和全表扫开销接近 - 对比两个查询的
Handler_read_next / Handler_read_key比值:比值越小,说明索引定位越准、扫描越少;比值 > 10 常提示需要收紧查询条件或调整索引结构 -
Handler_read_prev显著升高,通常对应ORDER BY ... DESC且无法利用索引倒序扫描(比如联合索引只支持单向 B+ 树遍历),这时考虑添加DESC显式索引(MySQL 8.0+ 支持)
必须配合 FLUSH STATUS + 单次执行,否则数据毫无意义
这些 Handler_read_* 计数器是全局累加的,不重置就查,看到的可能是过去几小时所有请求的混合结果,完全无法定位具体 SQL 的行为。真实诊断中,漏掉 FLUSH STATUS 是最常踩的坑。
- 标准操作流只有三步:
FLUSH STATUS→ 执行一次目标 SQL(务必单次,禁用批量或连接池复用)→ 立刻执行SHOW STATUS LIKE 'Handler_read%' - 不要依赖
SHOW GLOBAL STATUS,它反映的是整个实例历史累计值;用SHOW SESSION STATUS更精准,但前提是你的测试连接没被复用 - 如果应用使用连接池(如 HikariCP),测试前需确保连接是新建立的,或手动执行
RESET QUERY CACHE(虽已弃用)和FLUSH STATUS组合,避免状态污染
Handler_read_key 和 Handler_read_rnd_next 的数量级关系比任何 EXPLAIN 的 rows 估算都可靠;但它们不会告诉你“为什么没走索引”,只负责诚实报数——那部分得靠 optimizer_trace 或字段类型/字符集一致性检查来补全。


















