覆盖索引是什么
查询需要的列全部都在索引里时,存储引擎可以直接从索引返回结果,不必回表读聚簇索引,这就是覆盖索引。MySQL 的 EXPLAIN 会在 Extra 显示 Using index;InnoDB 的二级索引尾部隐含主键列,因此只查主键的语句常常天然被覆盖。
基本信息
| 判定依据 | EXPLAIN 的 Extra 出现 Using index |
|---|---|
| InnoDB 特性 | 二级索引隐含主键列,查主键常可被覆盖 |
| PostgreSQL | 对应 Index Only Scan,可用 INCLUDE 附加非键列 |
| 代价 | 索引变宽会放大存储占用与写入维护成本 |
| 写法要求 | SELECT 明确列名,SELECT * 很难达成覆盖 |
| 无效场景 | 查询列不在索引里就必须回表 |
MySQL 判定示例
MySQL 判定示例
ALTER TABLE orders ADD INDEX idx_user_created (user_id, created_at);
-- id 是主键:二级索引里隐含主键,这个查询可以完全被覆盖
EXPLAIN SELECT id, user_id, created_at
FROM orders
WHERE user_id = 100
ORDER BY created_at; -- Extra: Using index
-- 加上 total 列后需要回表,除非把 total 也放进索引
EXPLAIN SELECT total FROM orders WHERE user_id = 100;PostgreSQL 写法
PostgreSQL 写法
-- 对应的是 Index Only Scan
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);
EXPLAIN ANALYZE SELECT user_id, created_at FROM orders WHERE user_id = 100;
-- 用 INCLUDE 把非键列附加到索引上,省掉回表
CREATE INDEX idx_orders_cover ON orders (user_id) INCLUDE (created_at);
-- 注意 Index Only Scan 依赖可见性映射,需要例行清理维护实现要点
| 维度 | 说明 | 影响 |
|---|---|---|
| 判定方式 | EXPLAIN 里是否出现 Using index | 最直接的验证手段 |
| 列选择 | 把 SELECT 与 WHERE 用到的列纳入索引 | 列越多索引越宽 |
| 列顺序 | 过滤列在前,排序列在后 | 顺序合适才能兼顾过滤与排序 |
| 成本 | 索引体积与写入放大同步上升 | 写多读少的表要权衡 |
| PG 差异 | Index Only Scan 加 INCLUDE | 依赖 VACUUM 维护可见性映射 |
典型使用场景
- 高频窄查询:列表页只需要少数几列时
- 过滤加排序的组合查询,用复合索引一次满足
- 报表类查询:把统计所需的关键列放进索引
- 高并发点查:状态查询、首页展示这类走索引即可返回
- 避免大字段回表:明细正文留在表里,索引只覆盖必要列
- 替代方案评估:比新增冗余列更省空间的做法
PostgreSQL 的 Index Only Scan 需要可见性映射支持:如果表长期没有清理,索引里即使有数据仍可能回表读堆表,例行 VACUUM 才能让它真正发挥效果。
不要为覆盖而无限加列:索引越宽,写入与更新的维护代价越高,缓冲池里能缓存的索引页也更少。按真实查询形态取舍,并用执行计划确认效果。