select count()走索引仍慢,本质是低选择性索引导致大量回表或优化器弃用;应通过handler_read_验证真实执行路径,改用高选择性字段前置的复合索引或覆盖索引优化。
为什么 SELECT COUNT(*) 走了索引却还是慢?——选择性低的索引本质问题
索引选择性低,不是“建了没用”,而是“用了也白用”。核心在于:MySQL 优化器发现走这个索引后仍需回表大量数据,不如直接全表扫描。典型场景是给 status、is_deleted、gender 这类只有几个取值的字段建了单列索引。即使 EXPLAIN 显示 type=ref 或 range,实际执行时也可能因 rows 预估过大而放弃使用,或虽用了但 I/O 开销不降反升。
判断索引是否真被用上:别只看 EXPLAIN,要查 Handler_read_*
优化器决策依赖统计信息,而 EXPLAIN 只是预估。真正验证是否走索引,得看运行时的存储引擎计数器:
SHOW STATUS LIKE 'Handler_read%';
重点观察:Handler_read_key(索引查找次数)和 Handler_read_next(索引顺序扫描次数)是否明显上升;同时对比 Handler_read_rnd_next(回表随机读次数)是否暴增——如果后者远高于前者,说明索引虽被选中,但导致大量回表,性能反而恶化。
- 执行查询前先
FLUSH STATUS - 再跑一次目标 SQL
- 立刻查
Handler_read_*,比看EXPLAIN的key字段更可靠
提升选择性的实操路径:不是删索引,而是换建法
对低选择性字段,强行加单列索引基本无效。有效做法是把它作为复合索引的「后缀」,而非前导列: