EXPLAIN显示走索引但rows很大,说明索引选择性差或数据倾斜;type为ref/range却慢,多因回表开销大;函数或隐式转换会使索引失效。

EXPLAIN 显示用了索引,但 rows 值很大
这说明 MySQL 确实走了索引,但扫描行数远超预期。常见原因是索引选择性差或数据倾斜严重。
- 用
SELECT COUNT(DISTINCT column_name) / COUNT(*)算字段选择性,低于 0.1 的字段单独建索引效果有限 - 比如
status只有'active'/'inactive'两个值,即使建了索引,查WHERE status = 'active'仍要扫一半数据 - 复合索引中把高选择性字段放前面,例如
(user_id, created_at)比(created_at, user_id)更容易命中窄范围
type=ref 或 range,但实际执行很慢
这往往暴露回表开销过大——尤其当 SELECT * 配合非聚簇索引时。
- InnoDB 辅助索引只存主键值,查
SELECT *必须回表取完整行,每行一次随机 I/O - 解决办法是改成覆盖索引:把 SELECT 中用到的字段都包含进索引,例如原查
SELECT name, email FROM users WHERE user_id = ?,就建INDEX idx_user_id_cover (user_id, name, email) - 注意:
TEXT/BLOB类型字段不能加入索引,大字段会直接让索引失效或膨胀
WHERE 条件写了函数或隐式转换
索引列被包裹在函数里,或类型不匹配,会导致优化器放弃使用索引。
-
WHERE DATE(create_time) = '2024-01-01'→ 改成WHERE create_time >= '2024-01-01' AND create_time -
WHERE phone = 13800138000(phone 是VARCHAR)→ 必须写成WHERE phone = '13800138000',否则触发隐式转换 -
WHERE name LIKE '%张'→ 前导通配符无法用 B+ 树索引,要么改用全文索引,要么加前缀索引如INDEX idx_name_prefix (name(10))
优化器没选你建的索引
即使索引存在,MySQL 也可能因为统计信息过期或成本估算偏差而跳过它。
- 运行
ANALYZE TABLE table_name更新统计信息,尤其在大批量 INSERT/DELETE 后 - 检查
SHOW INDEX FROM table_name确认索引状态是否为usable,不是disabled - 小表(如几百行)全表扫描可能比走索引更快,优化器会主动绕过索引——这时建索引反而浪费空间和写入性能


















