加索引后delete仍慢,因每行删除需同步更新主键及所有二级索引,索引越多io放大越严重;且where条件若引发隐式转换、函数运算或未走索引,仍导致全表扫描或版本链清理压力。

为什么加索引后DELETE还是慢
加了索引不代表DELETE就快——如果WHERE字段有索引,但表上还挂着4个没用的二级索引,InnoDB删1行就得更新5棵树(主键+4个二级索引),实际IO放大5倍。更常见的是:索引建在create_time上,但查询条件写成DATE(create_time) ,函数导致索引失效,又退回全表扫描。
删前必须检查的索引清单
别急着建新索引,先砍掉拖后腿的旧索引:
- 用
SHOW INDEX FROM your_table列出全部索引 - 查
performance_schema.table_io_waits_summary_by_index_usage,过滤COUNT_STAR = 0的索引——这些从没被WHERE/JOIN/ORDER BY用过 - 重点盯
idx_user_id_status这类“看起来有用但实际冗余”的复合索引:如果已有idx_user_id和idx_status,三字段索引大概率白占空间 - 删完立即执行
ALTER TABLE your_table DROP INDEX idx_unused,别等“后面再优化”
该建什么索引才真管用
针对删除场景,索引设计要服从一个原则:让WHERE条件能走索引+减少回表+避免排序开销。
- 单字段删除(如
WHERE create_time ):建<code>KEY idx_create_time (create_time),不要加其他字段 - 多条件删除(如
WHERE status = 'cancelled' AND create_time ):把高区分度字段放前面,比如<code>KEY idx_status_ctime (status, create_time),别反过来 - 分批删除时用主键边界:确保
id是主键或带覆盖索引,否则ORDER BY id LIMIT 1000会触发filesort - 避免在索引字段上做计算:
WHERE YEAR(create_time) = 2024永远走不了索引,改成WHERE create_time >= '2024-01-01' AND create_time
索引重建后DELETE仍卡住?看这三点
索引只是基础,真正卡住常发生在索引之外:
-
autocommit必须为1,否则每批DELETE都在同一个事务里累积undo log,撑爆内存 - 确认
innodb_buffer_pool_size够大——如果缓冲池装不下要删的索引页,就会频繁刷脏页,IO直接拉满 - 检查是否有长事务未提交:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60,这种事务会阻塞MVCC清理,让DELETE反复清理版本链
真正容易被忽略的是:索引删减和重建必须在低峰期操作,且每次只动一个索引。线上表加删索引本身就会锁表(MySQL 5.6+对DROP INDEX已支持在线,但ADD INDEX仍可能锁写),而并发写入下索引维护的开销会实时反馈到DELETE延迟上。











