覆盖索引生效的唯一标准是EXPLAIN中Extra列为Using index;Using index condition仅表示索引下推,仍需回表;SELECT *、函数、NULL判断、TEXT/BLOB字段或索引顺序不匹配均会导致失效。

覆盖索引能彻底避免回表,但必须满足“所有查询字段都在索引中”且“不破坏最左前缀和表达式规则”,否则EXPLAIN里看不到Using index。
怎么确认覆盖索引生效了
只看EXPLAIN输出的Extra列:出现Using index才表示真正覆盖;Using index condition说明用了索引下推,但仍在回表;空值或Using where基本等于白建索引。
- 常见错误现象:WHERE用
idx_name,SELECT却多查了一个age字段,哪怕age有单独索引,也照样回表 - 函数会让覆盖失效,比如
WHERE YEAR(create_time) = 2023,即使create_time在索引里,也不触发Using index -
SELECT *几乎永远无法被覆盖(除非走主键索引),因为聚簇索引才存全行,二级索引叶子节点只存索引列+主键
联合索引字段顺序怎么排
顺序不是按字母或重要性,而是按查询模式中的执行逻辑:等值条件放最左,范围条件紧随其后,SELECT和ORDER BY字段放右边。
- 例如高频查询是
SELECT status, amount FROM orders WHERE user_id = ? AND create_time BETWEEN ? AND ? ORDER BY create_time DESC,对应索引应为ALTER TABLE orders ADD INDEX idx_user_time_status_amount (user_id, create_time, status, amount) - 如果把
create_time放最左,user_id = ?就无法利用索引前缀,整个索引对这个查询无效 - 排序方向要一致:
ORDER BY create_time DESC要求索引定义里也声明create_time DESC(MySQL 8.0+支持,5.7仅支持ASC)
哪些字段不该塞进覆盖索引
加字段不是越多越好,大字段和低频字段会显著拖慢写入、增大B+树层级、降低缓存命中率。
- 禁止包含
TEXT、BLOB、超长VARCHAR(如长度>255),MySQL不允许索引长度超3072字节,且这类字段会让单页存储更少记录 - 避免把
description、content等宽字段放进高频查询索引,哪怕业务偶尔需要,也该拆成两次查询:先查ID+关键字段,再按需回表取详情 - 用
SHOW INDEX FROM table_name对比index_length和data_length,若索引总大小接近数据大小,说明索引已过度膨胀,需清理冗余字段
为什么有时候加了覆盖索引还是慢
覆盖索引解决的是“要不要回表”,但不解决“查多少行”,大量匹配行仍会带来高I/O和CPU压力。
- 深度分页是典型陷阱:
SELECT id, name FROM user LIMIT 10000, 20即使有覆盖索引,也要扫描前10020行,建议改用游标:WHERE id > 12345 ORDER BY id LIMIT 20 - 选择性差的字段(如
status IN (0,1))放在索引最左,会导致索引扫描行数爆炸,应优先用高区分度字段(如tenant_id、city)过滤 - 主键设计不合理会放大回表代价:用UUID做主键会使聚簇索引页分裂严重,即使覆盖索引生效,单次索引访问的物理I/O延迟也可能翻倍
覆盖索引不是银弹——它只消灭“回表动作”,但不降低“扫描基数”或“磁盘寻道成本”。真正压测前,务必用EXPLAIN FORMAT=JSON看rows_examined和filtered,比盲目堆字段有用得多。


















