在数据库优化中,覆盖索引是提升查询性能的重要手段。它指的是查询所需要的所有字段都包含在索引中,数据库可以直接从索引拿到数据,不需要回表查询聚簇索引。但实际开发中,很多人建了合适的索引,执行计划却显示没有用到覆盖索引,这往往和 SQL 的写法有关,尤其是 SELECT 后面跟的内容。

什么是覆盖索引
假设我们有一张用户表,在 age 和 name 上建立了联合索引。如果一条查询只需要这两个字段,并且过滤条件用到索引前缀,那么数据库在遍历索引时就能拿到全部所需数据,这就是覆盖索引。
常见错误写法:使用 SELECT *
下面这条语句虽然用到了 age 上的索引做过滤,但写了 SELECT *:
-- 建立索引 CREATE INDEX idx_age_name ON user(age, name); -- 错误写法:SELECT * 会取出所有列 SELECT * FROM user WHERE age = 20;
由于表中可能还有 address、phone 等不在索引里的字段,数据库为了凑齐 SELECT * 要求的全部列,必须拿索引中的主键再去聚簇索引回表查询,覆盖索引因此未命中。
正确写法:只选必要列
把查询改成只选取索引包含的列:
-- 正确写法:只选索引中的列 SELECT age, name FROM user WHERE age = 20;
此时 age 和 name 都在 idx_age_name 里,查询完全不需要回表,执行计划中的 Extra 会显示 Using index,覆盖索引生效。
对比总结
| 写法 | 是否回表 | 覆盖索引 |
|---|---|---|
| SELECT * | 是(当存在非索引列时) | 未命中 |
| 只选必要列 | 否 | 命中 |
其他需要注意的点
- 即使只选一列,如果该列不在索引中,也无法命中覆盖索引。
- 使用函数包裹索引列(如
WHERE YEAR(age) = 20)会破坏索引使用,间接影响覆盖。 - 查询中若包含 ORDER BY 或 WHERE 用到的列不在同一索引,也可能无法覆盖。
覆盖索引未命中的核心原因,是查询所需的列超出了索引所能提供的范围,SELECT * 是最典型的引发回表的写法。
因此在写查询时,应当明确业务需要的字段,避免使用 SELECT *,并合理设计联合索引的顺序,把高频查询字段放在索引中,才能让覆盖索引真正发挥作用。