索引优化是权衡读写性能与空间消耗,核心在于索引是否被正确使用;需满足最左前缀匹配、避免隐式转换和函数操作;低选择性、极少查询、高频更新字段不宜建索引。

索引优化不是“加索引”,而是“加对索引”
很多人一听说“优化查询慢”,第一反应就是 CREATE INDEX,结果表变卡了、磁盘爆了、慢查还在——因为索引优化的核心从来不是“有没有索引”,而是“这个索引是否被用上、是否选得准、是否拖累写入”。它本质是权衡:用多少空间换多少读性能,牺牲多少写速度保多少查效率。
哪些 WHERE 条件才可能走索引?
MySQL 的 B+Tree 索引只对符合“最左前缀匹配”的条件生效。比如你建了复合索引 INDEX idx_user_status_city (status, city, age),以下查询能用上索引:
-
WHERE status = 'active'✅(匹配最左列) -
WHERE status = 'active' AND city = 'shanghai'✅ -
WHERE status = 'active' AND city > 'beijing' AND age = 25✅(范围查询后,age 仍可做等值过滤)
但这些基本失效:
-
WHERE city = 'shanghai'❌(跳过最左列status) -
WHERE status = 'active' OR city = 'shanghai'❌(OR易导致索引失效,除非两边都有独立索引且满足特定条件) -
WHERE status LIKE '%active'❌(前导通配符无法利用 B+Tree 有序性)
为什么加了索引,EXPLAIN 还显示 type=ALL?
常见原因不是语法错,而是隐式类型转换或函数包裹让索引“隐身”:
-
WHERE user_id = '123'→ 如果user_id是INT类型,MySQL 会把字符串转成数字再比对,但索引字段实际存储的是整型值,可能导致索引失效(尤其在旧版本或字符集不一致时) -
WHERE DATE(create_time) = '2025-01-01'→ 对索引列用函数,等于放弃索引结构,改用全表扫描 -
WHERE status IN ('active', 'pending') AND status != 'pending'→ 复杂逻辑可能干扰优化器选择,建议简化条件
验证方法永远是 EXPLAIN SELECT ...,重点看 type(应为 ref/range/const)、key(实际使用的索引名)、rows(预估扫描行数)。
什么时候不该加索引?
索引不是越多越好。以下情况加了反而有害:
- 低选择性字段:比如
gender(只有 'M'/'F'/NULL),索引区分度太低,优化器大概率直接全表扫描 - 极少被 WHERE/JOIN/ORDER BY 引用的列:纯冗余索引,占空间又拖慢 INSERT/UPDATE
- 频繁更新的字段:每次修改都要重排 B+Tree 叶子节点,IO 压力陡增
- 单表索引超 64 个(InnoDB 限制)或单个索引字段超 16 个:MySQL 拒绝创建,或报错
ERROR 1071: Specified key was too long
真正该优先建索引的,是那些出现在高频查询 WHERE、JOIN ON、ORDER BY、GROUP BY 中,且基数高、更新少的字段组合——比如订单表的 (user_id, status, created_at)。
最常被忽略的一点:索引不是一劳永逸的。数据分布变化(比如某状态从 5% 升到 95%)、查询模式迁移、统计信息陈旧,都可能让原本高效的索引突然“失灵”。定期用 ANALYZE TABLE 更新统计信息,比半夜手动重建索引更稳妥。










