快速识别冗余索引需结合执行覆盖与使用统计:已有(a,b)时单独建a即冗余;通过pt-duplicate-key-checker、sys.schema_unused_indexes或performance_schema.table_io_waits_summary_by_index_usage定位零使用索引;删前须验证EXPLAIN key列、慢日志及Handler指标,避免隐式依赖失效。

怎么快速识别冗余索引
冗余索引不是“看起来像”,而是实际执行中被更优索引覆盖。比如已有 (user_id, status),又单独建了 user_id —— 后者在所有等值查询 WHERE user_id = ? 场景下都多余。
常见错误现象包括:SHOW INDEX FROM table_name 显示多个索引的 Cardinality 极低(远小于表行数),或 sys.schema_unused_indexes(MySQL 8.0+)返回大量 rows_selected = 0 的索引。
实操建议:
- 用
pt-duplicate-key-checker扫描全库,它能直接标出哪些索引可被另一个包含(例如(a)和(a, b)共存时,(a)就是冗余) - 检查
performance_schema.table_io_waits_summary_by_index_usage,重点关注count_star = 0或rows_selected = 0的索引 - 别只看单列索引数量,重点看 WHERE 条件组合:如果从不单独查
b,只查a = ? AND b = ?或a = ?,那(a, b)足够,(b)必删
删索引前必须确认的三件事
直接 DROP INDEX 可能导致线上查询变慢,尤其当应用隐式依赖某个“看似冗余”但被 ORM 自动生成的索引时。
实操建议:
- 先查慢查询日志,确认近7天内没有语句用到待删索引的
key字段(EXPLAIN输出中的key列) - 在低峰期执行
ALTER TABLE ... DROP INDEX,并观察后续 1 小时的Handler_read_next和Handler_read_rnd_next指标是否突增 - 对核心表,删之前用
SELECT COUNT(*) FROM table WHERE indexed_col = ?模拟验证——确保该条件仍能走其他复合索引(如(indexed_col, other_col))
批量插入时临时关索引的边界条件
SET unique_checks = 0 和 SET foreign_key_checks = 0 确实能提速,但只适用于**可信数据源 + 单次导入**场景,不是常规写入的解法。
实操建议:
- 仅限离线导入、ETL 或灾备恢复等可控场景;线上服务实时 INSERT 绝对不能关
- 关闭后必须配
SET autocommit = 0,否则每条语句仍会触发独立日志刷盘 - 导入完成后立即执行
SET unique_checks = 1; ANALYZE TABLE table_name;,否则优化器统计信息不准,后续查询可能选错索引 - 注意:关
unique_checks不影响主键索引维护,只跳过唯一约束校验;外键检查关闭后,不会同步更新外键索引,所以导入完要重建相关索引
复合索引顺序错位的真实代价
把范围查询列(>、BETWEEN)放在等值查询列(=)前面,会导致后续列完全失效。例如 WHERE created_at > '2025-01-01' AND user_id = 123,若索引是 (created_at, user_id),则 user_id 部分无法用于查找,只能过滤。
实操建议:
- 等值列优先排左,高选择性列(如
user_id)放最左;范围列放右,且后面不再跟其他字段 - 含
ORDER BY时,排序字段必须连续出现在索引末尾,且方向一致(MySQL 8.0+ 支持DESC,5.7 不支持) - 用
EXPLAIN看key_len:如果查询条件用了 3 个字段,但key_len只显示前 2 个字段长度,说明第 3 个字段没走索引查找



















