索引是否生效取决于where、order by等子句是否符合最左前缀等规则,而非select本身;失效主因包括函数操作、隐式转换、跳过最左列及非覆盖查询,需结合explain验证。

SELECT 语句本身不决定是否走索引,真正起作用的是它背后的 WHERE、ORDER BY、JOIN 和返回字段——索引加速的不是“查”,而是“找”和“拿”。
WHERE 条件怎么写才能命中索引
索引只对满足匹配规则的 WHERE 条件生效。常见失效场景比你想象中更频繁:
- 对索引列用函数:
WHERE YEAR(create_time) = 2023→ 改成WHERE create_time >= '2023-01-01' AND create_time - 隐式类型转换:
user_id是VARCHAR,但写成WHERE user_id = 123→ 必须写成WHERE user_id = '123' - 前导通配符:
LIKE '%abc'完全无法用索引;LIKE 'abc%'可以 -
OR条件里只要有一个分支没索引,整个条件大概率退化为全表扫描
复合索引为什么必须遵守最左前缀
假设你建了 INDEX (status, create_time, user_id),那它只对以下查询有效:
WHERE status = 1WHERE status = 1 AND create_time > '2026-01-01'WHERE status = 1 AND create_time > '2026-01-01' AND user_id = 1001
但这些不行:
-
WHERE create_time > '2026-01-01'(跳过了status) -
WHERE user_id = 1001(最左列完全没出现) -
WHERE status = 1 AND user_id = 1001(中间跳过了create_time,user_id索引部分失效)
顺序不是“哪个常用放前面”,而是“哪个过滤性最强且总出现在查询开头”放最左。
覆盖索引:让 SELECT 不用回表
普通索引只存键值+主键指针,查完还得回主键索引捞数据——这叫“回表”。千万级数据时,回表 I/O 成本可能占查询耗时 70% 以上。
把 SELECT 要的字段也塞进索引,就能避免回表:
- 原查询:
SELECT name, phone FROM user WHERE status = 1 AND create_time > '2026-01-01' - 低效索引:
INDEX(status, create_time)→ 找到记录后还要回表取name和phone - 高效覆盖索引:
INDEX(status, create_time, name, phone)→ 所有字段都在索引里,执行计划显示Using index
注意:别把所有字段都加进去。索引越大,INSERT/UPDATE 越慢,维护成本越高。只加高频查询里真正需要的字段。
EXPLAIN 是唯一能验证索引是否生效的工具
光看 SQL 写得“像走索引”没用,必须跑 EXPLAIN 看执行计划:
-
type字段是关键:ref、range、const表示走了索引;ALL就是全表扫描 -
key显示实际使用的索引名,为空说明没走索引 -
Extra里出现Using filesort或Using temporary,说明ORDER BY或GROUP BY没利用上索引
哪怕加了索引,优化器也可能因统计信息不准或代价估算偏差而放弃使用——这时要 ANALYZE TABLE 更新统计信息,或用 FORCE INDEX 强制(慎用)。
索引不是魔法开关,它是精确匹配的“导航图”。写错一个条件、少一个字段、顺序颠倒一列,整张图就失效。真正难的不是建索引,而是读懂自己写的每一条 SELECT 到底在数据库里走了哪条路。











