MySQL联合索引严格遵循最左前缀法则,必须从最左列开始匹配;避免在索引列上做计算或函数操作;覆盖索引需包含SELECT和WHERE所有字段;务必用EXPLAIN验证索引实际使用情况。

联合索引必须从最左列开始匹配
MySQL 的联合索引不是“任意列都能用”,而是严格遵循最左前缀法则。比如你建了 idx_profession_age(profession, age),那只有以下条件能走这个索引:
WHERE profession = '工程师'WHERE profession = '工程师' AND age > 25WHERE profession = '工程师' AND age BETWEEN 25 AND 30
但只要跳过 profession,比如只查 WHERE age = 28 或 WHERE age = 28 AND city = '杭州',这个联合索引就完全失效——优化器会直接放弃,走全表扫描。
容易踩的坑:有人以为“反正 age 在索引里,查它总该快点”,其实 B+Tree 是按左侧字段排序构建的,没有 profession 值,就无法定位到子树起点。别指望 MySQL 自动重排索引顺序来适配你的查询。
避免在索引列上做任何计算或函数操作
哪怕只是加个函数、转个类型、算个表达式,都会让索引失效。因为索引树里存的是原始值,不是计算结果。
- ❌ 失效:
WHERE YEAR(create_time) = 2023、WHERE price * 1.1 > 100、WHERE SUBSTR(name, 1, 3) = '张三' - ✅ 有效:
WHERE create_time >= '2023-01-01' AND create_time 、<code>WHERE price > 90.91、WHERE name LIKE '张三%'
特别注意隐式转换:如果 id 是 VARCHAR 类型,却写成 WHERE id = 123(数字),MySQL 会把所有字符串转成数字比对,索引同样失效。务必保证类型一致,字符串就加引号:WHERE id = '123'。
覆盖索引能省掉回表,但得主动设计
覆盖索引不是自动发生的,它要求 SELECT 的所有字段 + WHERE 条件字段,全部包含在同一个索引中。这时 MySQL 只扫索引页,不查主键聚簇索引,I/O 大幅下降。
- 假设你常查:
SELECT user_id, status, created_at FROM orders WHERE user_id = 123 AND status = 'paid' - 那就应该建:
CREATE INDEX idx_user_status_created ON orders(user_id, status, created_at)
别写 SELECT *:它几乎永远破坏覆盖索引,因为主键以外的字段大概率不在索引里。另外,联合索引里字段顺序仍有讲究——等值条件列放前,范围查询列放后,最后放 SELECT 需要的额外字段(如 created_at)。
用 EXPLAIN 看清索引到底有没有被用
光看 SQL 写得“像”用了索引没用,必须跑 EXPLAIN 确认。重点关注三处:
-
key字段非NULL:说明真用了哪个索引 -
type至少是ref或range:ALL就是全表扫描,index是全索引扫描,都不理想 -
Extra里没有Using filesort或Using temporary:说明排序/分组没走索引
一个常被忽略的细节:key_len 值太小,可能意味着联合索引只用到了左边一部分。比如索引是 (a,b,c),但 key_len 只显示 a 的长度,说明 b 和 c 没参与匹配——往往是因为 a 条件用了范围(如 a > 10),导致后续列无法继续走索引。


















