覆盖索引能避免回表,因为SELECT及WHERE/ORDER BY/GROUP BY所需字段全部包含在索引中,MySQL可直接从二级索引叶子节点获取数据,无需访问聚簇索引。

覆盖索引为什么能避免回表
覆盖索引的核心在于:SELECT 语句中查询的所有字段,都包含在某个索引的列中(包括 WHERE、ORDER BY、GROUP BY 涉及的列),这样 MySQL 就无需访问聚簇索引(即主键 B+ 树)的叶子节点去捞数据行,直接从二级索引叶子节点就能拿到全部所需信息。
关键点是「全部所需字段都在索引里」——不是「有索引就行」,也不是「主键被包含就行」。比如 id 是主键,idx_name_age 是联合索引 (name, age),那么 SELECT name, age FROM user WHERE name = 'Alice' 是覆盖的;但 SELECT id, name FROM user WHERE name = 'Alice' 不是(id 不在该索引中,除非是主键索引本身)。
- 只有
SELECT列 +WHERE/ORDER BY/GROUP BY列全部被索引包含,才算覆盖 - 主键字段自动包含在所有二级索引中(InnoDB 的二级索引叶子节点存的是主键值),所以
SELECT中含主键时,只要主键是单列且被用作索引查找依据,就容易达成覆盖 -
SELECT *几乎不可能走覆盖索引(除非是主键索引本身,但那是全表扫描的特殊情况)
如何判断一个查询是否用了覆盖索引
看 EXPLAIN 输出里的 Extra 字段:出现 Using index 表示命中覆盖索引;如果同时出现 Using where 或 Using filesort,只要没出现 Using index condition 或 Using temporary,仍可能覆盖——关键是 Using index 必须存在。
注意陷阱:Using index condition(ICP)只是把部分 WHERE 下推到存储引擎层过滤,并不表示覆盖;而 Using index 才是真正只读索引不回表。
- 执行
EXPLAIN SELECT name, age FROM user WHERE name = 'Bob' AND age > 25;
,若key显示用了idx_name_age,且Extra含Using index,说明覆盖成功 - 如果
Extra是Using where; Using index,仍算覆盖(Using where表示 Server 层做了剩余过滤,但数据源仍是索引) - 如果
Extra是Using index condition,但没有Using index,说明只用了 ICP,仍要回表取数据
联合索引顺序对覆盖效果的影响
联合索引的列顺序决定了它能覆盖哪些查询模式。MySQL 只能高效利用索引的最左前缀,因此设计时要把「高频过滤字段」放前面,「排序/分组字段」居中,「SELECT 返回字段」尽量靠后补全——但最终必须保证所有查询涉及的字段都在这个顺序里连续出现(或通过主键隐式补全)。
由于疫情等原因大家都开始习惯了通过互联网上租车服务的信息多方面,且获取方式简便,不管是婚庆用车、旅游租车、还是短租等租车业务。越来越多租车企业都开始主动把租车业务推向给潜在需求客户,所以如何设计一个租车网站,以便在同行中脱颖而出就重要了,易优cms针对租车行业市场需求、目标客户、盈利模式等,进行策划、设计、制作,建设一个符合用户与搜索引擎需求的租车网站源码。 网站首页
例如,查询常为 WHERE status = ? AND created_at > ? ORDER BY score DESC,返回 id, title, score。此时建索引 (status, created_at, score, title) 是错的:虽然 score 被用于排序,但它在 WHERE 条件之后,无法跳过 created_at 单独加速 ORDER BY score;更优的是 (status, score, created_at, title),让 score 紧跟筛选条件,支持覆盖排序。
- 过滤字段优先级最高,必须放在最左
- 排序字段如果也参与
WHERE(如score BETWEEN 80 AND 100),可合并进前缀;否则应紧邻过滤字段,避免范围查询截断索引使用 - 只用于
SELECT的字段(如title)放在最后,它们不参与查找,只用于覆盖 - 不要为了覆盖而堆砌太多列——索引越宽,写入开销越大,缓存效率越低
覆盖索引不适合的典型场景
覆盖索引不是银弹。当查询需要高精度统计、大字段、或强一致性读时,强行覆盖反而有害。
比如 TEXT 或 BLOB 类型字段几乎不可能放进索引(会触发 Row size too large 错误);又比如 SELECT COUNT(*) FROM t WHERE deleted = 0,即使加了 INDEX(deleted),InnoDB 仍可能放弃覆盖而走聚簇索引扫描——因为二级索引不保存行是否存在删除标记(MVCC 版本链判断必须访问聚簇记录)。
-
SELECT包含TEXT/BLOB/ 大于 768 字节的VARCHAR字段,无法被索引覆盖 - 查询涉及
FOR UPDATE或LOCK IN SHARE MODE,覆盖索引无法加锁整行,MySQL 会退回到聚簇索引上加锁 - 使用了函数索引但
SELECT中对字段做了非索引化计算,如SELECT UPPER(name) FROM t WHERE name = 'alice',即使有name索引,UPPER()结果也不在索引里 - 分区表中跨多个分区的覆盖查询,优化器可能放弃使用覆盖索引
覆盖索引真正省的是「随机 IO」,但它的代价是更大的内存占用和更慢的写入。要不要建,得看读写比、字段宽度、以及那个 Using index 是否真的出现在你最关键的慢查询里。









