索引非万能,WHERE未用索引列、函数/隐式转换、返回大量行均致失效;复合索引需最左前缀连续匹配;覆盖索引可避免回表;唯一索引重在语义与锁优化;执行计划是否真实走索引才是关键。

为什么加了索引查询还是慢
索引不是万能的,WHERE 条件没用上索引列、用了函数或类型隐式转换、或者查询返回大量行,都会让索引失效。比如写 WHERE YEAR(create_time) = 2023,MySQL 无法使用 create_time 上的普通 B+ 树索引;又比如 WHERE user_id = '123',而 user_id 是 INT 类型,字符串比较会触发隐式转换,导致索引跳过。
实操建议:
- 用
EXPLAIN查看type是否为ref/range,key是否显示实际使用的索引名 - 避免在索引列上做计算、函数调用、
LIKE '%xxx'开头匹配 - 确保查询条件的数据类型和字段定义完全一致(如
INT对INT,不是INT对VARCHAR)
复合索引的最左前缀怎么生效
MySQL 的复合索引(如 (a, b, c))只支持从左到右连续匹配。这意味着它可以加速 WHERE a = ?、WHERE a = ? AND b = ?、WHERE a = ? AND b = ? AND c = ?,但对 WHERE b = ? 或 WHERE a = ? AND c = ?(跳过 b)无效。
实操建议:
- 把高频等值查询字段放最左,范围查询(
>、BETWEEN)字段放中间,排序/分组字段放最后 - 如果常查
status和created_at,但status只有 3 个值,而created_at高度离散,应建(status, created_at)而非反过来 - 不要盲目堆字段:超过 3 列的复合索引维护成本高,且命中率未必提升
什么时候该用覆盖索引
覆盖索引指查询所需的所有字段都包含在索引中,无需回表查聚簇索引。例如表有 id、name、email、status,而你执行 SELECT name, email FROM users WHERE status = 'active',若存在索引 (status, name, email),就能直接从索引页拿到全部数据。
实操建议:
- 对高频、轻量级查询(如列表页只展示几个字段),优先考虑覆盖索引减少 I/O
- 注意索引大小:把大字段(如
TEXT、长VARCHAR)放进索引会显著增大索引体积,得不偿失 - 用
EXPLAIN看Extra列是否含Using index,这是覆盖索引生效的明确信号
唯一索引和普通索引选哪个
唯一索引(UNIQUE)在写入时多一次重复值校验,但对读性能几乎无影响;普通索引允许重复,写入略快。真正关键区别在于语义和锁行为:唯一索引在 INSERT ... ON DUPLICATE KEY UPDATE 或 REPLACE INTO 场景下能精准定位冲突行,减少锁范围。
实操建议:
- 业务上要求唯一性的字段(如
email、order_no)必须建UNIQUE,既是约束也是性能保障 - 主键默认是聚簇唯一索引,不要额外建
UNIQUE(id) - 如果只是想加速查询,且字段天然不唯一(如
status),用普通索引即可,别强行加UNIQUE
索引优化最易被忽略的点:不是“有没有索引”,而是“查询是否真的走到了它”。很多慢查根源不在 SQL 写法,而在统计信息过期、索引碎片、或缓冲池未预热——这些不会报错,但会让执行计划严重偏离预期。











