handler_read_key高、handler_read_rnd_next低表明索引被有效利用,但需结合查询模式、索引结构和explain交叉验证;二者是存储引擎实际读取行为的累计值,非开关式计数器。

Handler_read_key 高、Handler_read_rnd_next 低,是索引被有效利用的直接信号;但单看这两个值不能断定索引设计合理——必须结合查询模式、索引结构和 EXPLAIN 结果交叉验证。
Handler_read_key 和 Handler_read_rnd_next 的真实含义
这两个指标不是“开关式”计数器,而是反映底层存储引擎实际读取行为的累计值:
-
Handler_read_key:每次通过索引定位到某一行(比如WHERE id = 123或JOIN ON idx_col = x)就+1,不管后续是否回表 -
Handler_read_rnd_next:每次在数据文件(.ibd)里按行号顺序读下一行,典型场景是全表扫描、ORDER BY RAND()、或索引无法覆盖排序/范围后被迫回表再取行 - 它们的比值可粗略估算索引命中率:
Handler_read_key / (Handler_read_key + Handler_read_rnd_next),低于 90% 就该警惕 - 注意:
Handler_read_rnd_next高 ≠ 一定没建索引,可能是索引建了但没被用上(如类型隐式转换、函数包裹字段)
为什么只清空一次 status 不够用?
MySQL 的 SHOW STATUS LIKE 'Handler_read%' 返回的是自上次 FLUSH STATUS 或服务启动以来的累积值。生产环境跑几天后,数值早已失真:
- 高并发下
Handler_read_rnd_next每秒涨几万,你看到的“123456789”毫无诊断意义 - 正确做法是:在业务低峰期执行
FLUSH STATUS,然后复现目标查询(比如压测一条慢 SQL),再立刻查差值 - 例如:
SELECT * FROM orders WHERE user_id = 123 AND status = 'paid' ORDER BY created_at DESC LIMIT 20执行前后对比Handler_read_key增量是否 ≥ 20,Handler_read_rnd_next是否为 0
哪些常见误操作会让 Handler 指标“看起来正常”但索引实际失效?
指标数字好看,不代表查询走对了路。这些情况会导致 Handler_read_key 有增长,但效率极低:
- 对索引列做函数操作:
WHERE YEAR(create_time) = 2025→ 即使create_time有索引,也退化为全表扫描,Handler_read_rnd_next爆涨 - 字符串字段类型不匹配:
user_id是BIGINT,但查询写成WHERE user_id = '123'→ 触发隐式转换,索引失效 - 联合索引顺序错位:
INDEX(a, b, c),但查询是WHERE b = 2 AND c = 3→Handler_read_key可能为 0,Handler_read_rnd_next暴增 - 使用了
OR且非同一索引覆盖:WHERE a = 1 OR b = 2,若只有INDEX(a)和INDEX(b),优化器常放弃索引走全表
真正要盯住的其实是 Handler_read_next 和 Handler_read_first
很多人只记 key 和 rnd_next,却忽略了这两个更敏感的信号:
-
Handler_read_first高:说明大量查询在做“全索引扫描”,比如SELECT id FROM t(id 有索引但无 WHERE)——这不是问题,但若伴随Handler_read_rnd_next高,就是索引未覆盖 SELECT 列,被迫回表 -
Handler_read_next持续上涨:代表正在用索引做范围扫描(WHERE a BETWEEN 1 AND 100)或排序扫描(ORDER BY a,b)。如果它远大于Handler_read_key,说明查询返回行数多、索引深度利用充分;但如果Handler_read_rnd_next也同步飙升,大概率是索引没包含SELECT字段,导致每行都得回表 - 一个典型坏信号:
Handler_read_first=1、Handler_read_next=10000、Handler_read_rnd_next=10000→ 全索引扫描后,对每一行都去聚簇索引捞数据,等于白建索引
最易被忽略的一点:Handler 指标反映的是“过去发生了什么”,而索引合理性取决于“未来会不会被正确使用”。哪怕当前所有指标都漂亮,只要存在 WHERE a = ? ORDER BY b 这类查询,而索引是 (a) 不是 (a,b),下次加个 LIMIT 就可能触发 Using filesort —— 那时 Handler_read_next 会猛增,Handler_read_rnd_next 跟着爆表。











