mysql中覆盖索引与普通索引的性能对比

2次阅读

覆盖索引能避免回表是因为二级索引包含查询所需全部字段,无需回聚簇索引查找整行数据;explain中extra显示“using index”即生效,且受联合索引最左前缀和列顺序严格限制。

mysql中覆盖索引与普通索引的性能对比

覆盖索引为什么能避免回表

mysql 的二级索引(非聚簇索引)只存储索引列 + 主键值,不存整行数据。当 select 的所有字段都包含在某个索引中时,优化器可以直接从该索引页拿到全部所需数据,无需再用主键去聚簇索引里查一遍——这个“免去二次查找”的过程就是“避免回表”。普通索引若缺失部分 SELECT 字段(尤其是非索引列),就必须回表,I/O 和 CPU 开销明显上升。

怎么判断一个查询是否走覆盖索引

EXPLAINExtra 列:

  • 出现 Using index → 覆盖索引生效
  • 只有 Using whereUsing index condition → 没覆盖,可能回表
  • 同时出现 Using index; Using where → 覆盖索引 + 条件过滤(仍不回表)

注意:即使用了 WHERE,只要所有 SELECT 字段和 WHERE 字段都在同一索引里,仍可覆盖。例如表 t(user_id, status, create_time),执行

SELECT user_id, status FROM t WHERE create_time > '2023-01-01'

就不会覆盖——因为 create_time 在索引里,但没被选中;而

SELECT user_id, create_time FROM t WHERE status = 1

可能覆盖,取决于索引定义顺序。

联合索引顺序对覆盖效果的影响

覆盖能力高度依赖索引列顺序。MySQL 按最左前缀匹配,且索引页内按定义顺序排序存储:

  • INDEX idx_a_b_c (a, b, c) 可覆盖:SELECT aSELECT a,bSELECT a,b,cSELECT a,c WHERE b = ?(注意:b 在中间,c 无法跳过 b 使用索引排序,但覆盖仍成立)
  • SELECT b,c 不会走该索引覆盖,因缺失最左列 a
  • 若常查 b,c,应建 INDEX idx_b_c (b, c),而非依赖旧索引

另外,ORDER BYGROUP BY 字段若不在覆盖索引中,即使 SELECT 覆盖了,也可能被迫排序或临时表,实际性能未必好。

覆盖索引的代价和误用风险

它不是银弹。每多一列进索引,B+ 树节点就更臃肿:

  • 写入变慢:INSERT/UPDATE 需维护更多索引数据
  • 空间翻倍:比如给大文本列加进索引,索引体积可能超过原表
  • 缓存压力:更大的索引页降低 buffer pool 命中率
  • 隐式类型转换会让覆盖失效:比如 WHERE status = '1'(status 是 int),触发隐式转换,可能导致索引无法使用,更别说覆盖

真正关键的是:别为了“看起来用了索引”而字段。先确认查询模式,再按 WHERE → ORDER BY → SELECT 优先级设计索引列顺序,最后看是否自然覆盖。强行把 5 个字段塞进一个索引,往往不如两个精简索引管用。

text=ZqhQzanResources