
怎么快速发现表里有重复索引
MySQL 本身不报错也不警告,但冗余索引会拖慢写入、浪费内存、让 EXPLAIN 分析更难读。真正有效的检查方式是查 information_schema,而不是靠肉眼扫 SHOW CREATE TABLE。
- 用
SELECT对比索引列组合:每个索引的seq_in_index必须严格一致才能算“完全重复”,比如(a,b)和(a,b,c)是包含关系,不算重复,但(a,b)和(b,a)在查询条件为WHERE a=1 AND b=2时效果接近,需人工判断是否冗余 - 执行这句就能揪出明显重复(同一张表、相同列顺序、相同类型):
SELECT table_name, index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index) cols FROM information_schema.statistics WHERE table_schema = 'your_db' GROUP BY table_name, cols HAVING COUNT(*) > 1;
- 注意
index_type(如BTREEvsHASH)和sub_part(前缀长度)不同,也会导致看似重复实则不可删——比如一个索引是name(10),另一个是name(20),就不能简单合并
删除冗余索引前必须确认的三件事
直接 DROP INDEX 可能导致慢查询突然爆发,尤其在没监控 SQL 模式的情况下。
- 查
performance_schema.table_io_waits_summary_by_index_usage(MySQL 8.0+)或用pt-index-usage工具回放 slow log,确认该索引是否真没人用——别只看“没被WHERE用”,ORDER BY或GROUP BY也可能依赖它 - 检查是否有唯一约束或主键隐式依赖这个索引:比如你删了
UNIQUE(a),但业务逻辑靠它防重,那INSERT IGNORE就会失效 - 确认存储引擎行为:InnoDB 的二级索引叶子节点存主键值,所以
(a)和(a,b)虽然列有重叠,但如果查询常需要b,留后者反而减少回表;而 MyISAM 没回表概念,冗余更纯粹
DROP INDEX 为什么有时会卡住或失败
不是权限或语法问题,而是 MySQL 内部锁和元数据变更机制在起作用。
- DDL 在 MySQL 5.6+ 默认是 online,但加/删索引仍需对表加
MDL_SHARED_ALTER锁,如果此时有长事务正在查这张表(哪怕只是SELECT),DROP INDEX就会等——用SELECT * FROM performance_schema.threads WHERE PROCESSLIST_COMMAND = 'Sleep'找出并 kill 掉可疑连接 - 分区表删索引要格外小心:
DROP INDEX idx ON tbl对全局有效,但如果你只在某个分区建了索引(通过ALTER TABLE ... REORGANIZE PARTITION带索引操作),得先确认索引是否跨分区 - 错误信息
ERROR 1091 (42000): Can't DROP 'idx'; check that column/key exists往往是因为索引名大小写不匹配(Linux 下文件系统敏感),或者用了反引号但没包全——比如`my_idx`和my_idx在某些版本里不算同一个
复合索引顺序错了,重建比删再建更安全
很多人想删掉 (b,a) 然后建 (a,b),但中间空窗期可能引发线上查询变慢。不如一步到位用 ALTER TABLE ... ALGORITHM=INPLACE 替换。
- MySQL 5.7+ 支持
ALGORITHM=INPLACE重建索引(前提是不改列类型、不跨引擎),速度比DROP + ADD快,且锁表时间极短——验证方法:执行前开一个事务BEGIN; SELECT * FROM t LIMIT 1;,再跑ALTER,看会不会被阻塞 - 命令示例:
ALTER TABLE users DROP INDEX idx_b_a, ADD INDEX idx_a_b (a,b);
这条语句内部会被优化成单次重建,不是两步 - 但注意:如果原索引是
UNIQUE,新加的也得带UNIQUE,否则约束丢失;另外FULLTEXT或SPATIAL索引不支持INPLACE,必须走 copy 方式
最麻烦的不是找冗余索引,是判断“这个索引到底服务了哪些查询”。没有慢日志或 performance_schema 开启习惯,光看 SHOW INDEX 就动手删,等于蒙眼调参。











