单列索引过多会显著拖慢DML操作:每增一个单列索引,INSERT/UPDATE/DELETE就得额外执行一次B+树维护,5个索引时耗时达1个时的2.8倍,8个时翻4.5倍;高频更新字段、大字段前缀索引、JSON生成列索引、低基数字段索引及冗余前导列索引危害最大。

单列索引过多会直接拖慢INSERT/UPDATE/DELETE
结论很明确:每多一个单列索引,每次DML操作就要多执行一次B+树插入/更新/删除。不是“可能变慢”,而是写入路径上硬编码的开销——InnoDB必须为每个二级索引单独定位、分裂页、刷脏、写redo。5个单列索引时,单行INSERT耗时常是1个索引时的2.8倍;到8个,实测翻4.5倍不罕见。
哪些单列索引最危险?看字段更新频率和数据类型
高频更新字段上的单列索引(如updated_at、status)代价最高,哪怕只改一个时间戳,所有含该字段的索引都得重算节点。更隐蔽的是大字段前缀索引:VARCHAR(2000)上建INDEX (content(255)),虽然存得少,但每次更新仍要重新计算哈希或截断值并刷新整页。
- JSON字段生成列上的单列索引:JSON内容一变,生成列值重算 → 所有依赖它的索引同步更新
- 低基数字段(如
gender、is_deleted)上的单列索引:Cardinality接近0,优化器基本不用,但DML照旧维护 - 已有复合索引前导列的单列索引:比如已有
INDEX (user_id, status),再建INDEX (user_id)就是纯冗余
用系统视图和慢日志验证真实影响
别猜,查证据。MySQL 8.0+ 直接跑:SELECT * FROM sys.schema_unused_indexes,看哪些单列索引近30天完全没被任何查询命中;同时结合performance_schema.table_io_waits_summary_by_index_usage确认DML是否真在刷它。再翻慢查询日志,如果INSERT或UPDATE频繁出现在top 10,且EXPLAIN FORMAT=JSON里used_columns显示只用了其中1–2个索引,其余全是白维护。
- 监控关键指标:
Innodb_row_lock_waits突增 +Handler_write与Handler_commit比值异常升高,说明索引维护锁竞争加剧 - DDL期间卡顿明显?
ALTER TABLE ... ADD INDEX要为每个单列索引单独扫描全表,索引数 × 行数 = 实际I/O压力
删之前必须盯住业务SQL的真实依赖
删一个INDEX (status)很容易,难的是确认没有报表或后台任务正靠它加速WHERE status = 'pending'这种查询。删前一定要用slow_query_log配合min_examined_rows = 1000抓一周流量,重点过滤含该字段的WHERE条件;对核心表,务必用pt-online-schema-change --alter "DROP INDEX idx_status"替代原生DROP INDEX,避免锁表。
真正容易被忽略的点是:索引删除后,优化器可能因统计信息未及时更新而继续沿用旧计划,导致某条SQL突然从Using index退化成Using where; Using filesort——这不是删错了,是没等ANALYZE TABLE生效就上线了。



















