冗余索引指一个索引的最左前缀被另一索引完全覆盖,丧失独立查询价值;如已有index(a,b,c),则index(a,b)冗余,而index(b,a)不冗余。

怎么判断两个索引是否冗余
MySQL 中的冗余索引,是指一个索引的列前缀已经完全被另一个索引覆盖,导致它无法提供额外的查询能力。比如已有 INDEX (a, b, c),再建 INDEX (a, b) 就是冗余的——因为前者能完全替代后者的所有使用场景。
关键看「最左前缀匹配」是否被覆盖:只要已有索引的前 N 列和新索引完全一致(且顺序相同),新索引就大概率冗余。
-
INDEX (user_id, created_at)和INDEX (user_id)→ 后者冗余 -
INDEX (status, updated_at)和INDEX (status, updated_at, id)→ 前者冗余 -
INDEX (a, b)和INDEX (b, a)→ 不冗余(顺序不同,适用场景不同) -
INDEX (a)和INDEX (a, b) WHERE b > 0→ 不冗余(后者是条件索引,覆盖场景不同)
如何用 SQL 找出潜在冗余索引
MySQL 5.7+ 可通过 sys.schema_redundant_indexes 视图快速定位。先确认该视图已启用(默认开启):
SELECT * FROM sys.schema_redundant_indexes;
结果中会列出 object_schema、object_name(表名)、redundant_index_name 和 dominant_index_name,后者是“压倒性”更强的那个索引。
注意:sys 库依赖 performance_schema,若关闭了相关采集项(如 events_statements_history_long),可能影响部分统计,但冗余判断本身不依赖运行时数据,一般可靠。
哪些索引组合特别容易踩坑
实际建表或优化时,这几类组合高频出现冗余,且常被忽略:
- 主键是
INT自增,又单独为该字段建INDEX (id)→ 完全多余(主键本身就是聚簇索引,且可被用于单列查找) - 联合索引包含主键列,例如
INDEX (tenant_id, id),而id是主键 → 若查询只用id,仍走不了这个索引;但若已有PRIMARY KEY (id),那(tenant_id, id)并不冗余,只是要注意它对tenant_id单查有效,对id单查无效 - 全文索引和普通索引混用:
FULLTEXT (title, content)和INDEX (title)→ 后者在全文检索场景下无意义,但若还有WHERE title = ?查询,则不能删,得看实际 SQL - 唯一索引 + 普通索引重复列:
UNIQUE (email)和INDEX (email)→ 后者绝对冗余(唯一索引自带 B+ 树结构,查询性能一致)
删索引前必须验证的三件事
即使确认冗余,也不能直接 DROP INDEX。生产环境必须核对:
- 是否有慢查询正依赖该索引?查
performance_schema.events_statements_summary_by_digest或慢日志里Using index的提示 - 是否被外键约束隐式使用?
SHOW CREATE TABLE tbl看FOREIGN KEY定义,MySQL 要求外键列必须有索引,且不能仅靠前缀索引覆盖(如INDEX (col(10))不行) - 是否在某个
ORDER BY + LIMIT场景中起到避免 filesort 的作用?例如WHERE a = ? ORDER BY b LIMIT 10依赖INDEX (a, b),而你只留了INDEX (a, b, c)—— 这没问题;但如果删掉的是INDEX (a, b),只留INDEX (b, a),那就失效了
冗余的本质不是“长得像”,而是“能力被白嫖”。哪怕列数、顺序、类型都对得上,也得拉出真实查询计划(EXPLAIN FORMAT=TREE)看 optimizer 实际选了谁。这点最容易被跳过。










