删除索引前必须确认它是否被查询实际使用
很多团队一看到“某个索引没被创建时的字段”就直接删,结果线上
SELECT变慢、
EXPLAIN显示全表扫描。MySQL 不会主动告诉你哪个索引正在被用——得自己查。最可靠的方式是开启
performance_schema中的
table_io_waits_summary_by_index_usage表(MySQL 5.6+),或用
sys.schema_unused_indexes视图(需启用
performance_schema并加载
sys库)。
注意:
INFORMATION_SCHEMA.STATISTICS或
SHOW INDEXES只告诉你“有”,不告诉你“用没用”。临时禁用索引(
ALTER TABLE ... DISABLE KEYS)在 InnoDB 中无效,别试。 检查是否真没用:
SELECT * FROM sys.schema_unused_indexes WHERE object_schema = 'your_db';若没开
performance_schema,至少跑一周慢查询日志 +
pt-index-usage工具分析 复合索引中部分字段被查询用到(比如
(a,b,c)上只查
a = ?),该索引仍算“被使用”,不能仅因
c没出现就删
DROP INDEX
语句本身会锁表(InnoDB 下取决于 MySQL 版本)
MySQL 5.6 之前,
DROP INDEX会触发表重建,整个过程阻塞 DML;5.7+ 支持
ALGORITHM=INPLACE的在线 DDL,但仍有条件限制:索引不是主键、不涉及全文/空间索引、且表引擎为 InnoDB。
执行前务必确认版本和存储引擎,否则可能在高峰期卡住业务写入。
安全写法(显式指定算法):ALTER TABLE t1 DROP INDEX idx_name, ALGORITHM=INPLACE, LOCK=NONE;若报错
ALGORITHM=INPLACE is not supported,说明不满足条件,得切到低峰期用
ALGORITHM=COPY(会锁表) 主键索引不能用
DROP INDEX删除,必须用
ALTER TABLE ... DROP PRIMARY KEY,且会重建聚簇索引,代价极高
删除索引后写入变快,但某些查询可能陡然变慢
索引本质是“以空间换时间”的冗余结构。删掉一个索引,
INSERT/
UPDATE/
DELETE的维护开销下降,但原本走索引的
WHERE、
ORDER BY、
JOIN可能退化为文件排序或临时表,CPU 和 I/O 压力反而上升。
尤其要注意覆盖索引场景:如果一个
SELECT a,b FROM t WHERE c=?原本命中
(c,a,b)覆盖索引,删掉后变成回表+排序,延迟可能翻几倍。 删索引前,用
EXPLAIN FORMAT=JSON对核心 SQL 重跑执行计划,比对
key、
rows、
extra字段变化 监控删索引后
Handler_read_rnd_next(随机读行数)和
Sort_merge_passes是否激增 批量导入场景下,可临时删非关键索引加速写入,但导入完必须立刻重建——别忘了
唯一索引与外键约束不能随便删
唯一索引(含主键)不只是性能结构,更是数据完整性保障。删掉唯一索引后,应用层插入重复值不会报错,直到唯一约束失效引发脏数据;外键索引被删会导致
ALTER TABLE ... DROP FOREIGN KEY失败,因为 MySQL 要求外键字段必须有索引支撑。
这类索引哪怕“看起来没被查询用”,也不能按普通索引处理。
检查是否为外键索引:SELECT CONSTRAINT_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 't' AND REFERENCED_TABLE_NAME IS NOT NULL;检查是否为唯一约束:
SHOW CREATE TABLE t中找
UNIQUE KEY或
PRIMARY KEY真要删,必须同步评估业务逻辑能否容忍重复、级联操作是否可控、下游服务是否依赖该约束语义
真正难的不是语法怎么写,是判断“这个索引到底是不是冗余”——它可能在某个凌晨三点的报表任务里悄悄扛着压力,也可能在某个未覆盖的异常分支里唯一生效。删之前,看数据,看执行计划,看监控曲线,别信名字,也别信直觉。
