必须先确认索引“未被使用”“不被依赖”“删后执行计划不变”三者缺一不可;需启用performance_schema及相关消费者与仪器,等待24–48小时后查COUNT_READ=0,并交叉验证外键、FORCE INDEX及EXPLAIN。

直接删索引风险极高,必须先确认它既“没被用过”,又“不被依赖”,再验证“删了不影响执行计划”——三者缺一不可。
查 performance_schema.table_io_waits_summary_by_index_usage 前先开开关
这个视图返回的 COUNT_READ = 0 不代表索引真没用,而是“启用监控后没被真正读过”。默认它根本没开,查出来全是 0 是假阴性。
必须依次执行:
-
SET GLOBAL performance_schema = ON;(如果还没启用) UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_waits_current', 'events_waits_history_long');UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'wait/io/table/%';
之后等至少 24–48 小时(覆盖业务高峰+低峰),再查:SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, COUNT_READ, COUNT_STAR FROM performance_schema.table_io_waits_summary_by_index_usage WHERE OBJECT_SCHEMA = 'your_db' AND COUNT_STAR = 0;
别只看 COUNT_STAR = 0,还要防三类“伪闲置”
有些索引在 table_io_waits_summary_by_index_usage 里是 0,但删了会立刻出事:
-
ORDER BY 场景:比如只有
SELECT * FROM logs ORDER BY created_at LIMIT 10,不带 WHERE,MySQL 用INDEX(created_at)避免 filesort,但不触发COUNT_READ -
外键隐式索引:
ALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(id);会自动建索引;删掉会导致后续加外键失败或主从同步卡住 -
FORCE INDEX 硬编码:代码里写了
SELECT /*+ USE INDEX(idx_user_status) */ ...或 ORM 层显式指定索引名,这种索引永远不进统计,但删了就报错
所以必须补查:SELECT CONSTRAINT_NAME, COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table' AND REFERENCED_TABLE_NAME IS NOT NULL;
并全局 grep "FORCE INDEX\|USE INDEX" 代码库和代理日志。
识别冗余索引:重点比前缀,不是比名字
冗余 ≠ 无用,而是“能被另一个索引完全替代”。常见误判是看到 idx_user_id 和 idx_user_id_status 就删前者——这没错;但看到 idx_user_id_status 和 idx_user_id_created_at 就乱删,就错了。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
正确做法是查字段顺序:
SELECT TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX, COLUMN_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'your_db' ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX;
然后人工比对:如果已有 INDEX(a, b, c),那么 INDEX(a)、INDEX(a, b) 都冗余;但 INDEX(a, c) 不冗余,因为优化器无法用 (a,b,c) 覆盖 WHERE a=1 AND c=2(中间跳过了 b)。
删除必须用 ALTER TABLE DROP INDEX,且选低峰期
DROP INDEX idx_name ON tbl_name 是 legacy 语法,MySQL 8.0+ 官方已标记为不推荐。它容易漏写 ON 导致语法错误,且语义模糊。
统一用:ALTER TABLE your_table DROP INDEX idx_name;
注意两点:
- 大表上删除二级索引虽支持
ALGORITHM=INPLACE(不锁表),但需确保innodb_online_alter_log_max_size足够,否则退化成 copy 表 - 主从架构下,DDL 会完整写入 binlog;建议先在从库
STOP SLAVE,主库删完观察延迟追平后再START SLAVE
最常被忽略的是:删完一个索引后,立刻跑一遍核心查询的 EXPLAIN,重点看 key 字段有没有变、rows 是否暴增、是否出现 Using filesort 或 Using temporary——这些才是真实影响。

















