索引实战
hardMySQL索引优化覆盖索引索引下推最左前缀
覆盖索引
如果一个查询所需的所有列都包含在索引中,就不需要回表,称为覆盖索引:
-- 有联合索引 (name, age)
SELECT name, age FROM users WHERE name = '张三';
-- 覆盖索引 ✅ 不需要回表
SELECT * FROM users WHERE name = '张三';
-- 非覆盖 ❌ 需要回表查其他列
最左前缀原则
联合索引 (a, b, c) 的排序方式是先按 a 排序,a 相同再按 b,b 相同再按 c。因此:
-- 能使用索引的查询:
WHERE a = 1 ✅ 使用 a
WHERE a = 1 AND b = 2 ✅ 使用 a, b
WHERE a = 1 AND b = 2 AND c = 3 ✅ 使用 a, b, c
-- 不能使用索引的查询:
WHERE b = 2 ❌ 跳过了 a
WHERE b = 2 AND c = 3 ❌ 跳过了 a
WHERE a = 1 AND c = 3 ⚠️ 只使用 a(c 跳过了 b)
索引下推(ICP)
MySQL 5.6+ 引入的优化。在使用联合索引时,将部分 WHERE 条件下推到存储引擎层过滤,减少回表次数:
-- 联合索引 (name, age)
SELECT * FROM users WHERE name LIKE '张%' AND age = 25;
-- 无 ICP:引擎返回所有 name 以"张"开头的行 → Server 层过滤 age
-- 有 ICP:引擎在索引中就过滤 age=25,只返回满足条件的行 → 回表更少
索引失效的常见场景
| 场景 | 示例 | 原因 |
|---|---|---|
| 对索引列使用函数 | WHERE YEAR(create_time)=2024 |
函数破坏了索引的有序性 |
| 隐式类型转换 | WHERE varchar_col = 123 |
数字与字符串比较,MySQL 转换 varchar 列 |
| LIKE 左模糊 | WHERE name LIKE '%三' |
无法利用索引的有序性 |
| OR 条件 | WHERE a=1 OR b=2 |
除非 a 和 b 都有索引 |
| 不满足最左前缀 | WHERE b=2(联合索引 a,b,c) |
跳过最左列 |
| NOT IN / != | WHERE status != 1 |
优化器可能认为全表扫描更快 |
生产高频题
什么是覆盖索引?
查询所需的所有列都在索引中,不需要回表到聚簇索引。通过 EXPLAIN 查看 Extra 列显示 Using index 即为覆盖索引。
什么情况下索引会失效?
对索引列用函数计算、隐式类型转换、LIKE 左模糊、不满足最左前缀原则、使用 OR 连接不同列等。可以用 EXPLAIN 检查执行计划中的 type 和 key 字段判断索引是否生效。