MySQL索引不会因DELETE失效,但大量删除会导致B+树出现空洞页、统计信息陈旧,引发执行计划劣化;需通过INNODB_INDEX_STATS和SHOW INDEX验证统计准确性,再决定是否重建。

大规模 DELETE 后为什么索引“变慢”而不是“失效”
MySQL 的索引不会因 DELETE 操作而“失效”,但大量删除(尤其非顺序、高比例删除)会导致 B+ 树索引出现大量“空洞页”(empty pages)、页分裂不均、统计信息陈旧,最终表现为查询执行计划劣化(如误选全表扫描)、EXPLAIN 显示 rows 严重高估、key_len 异常变小等现象。本质是数据物理分布碎片化 + 优化器统计失真。
判断是否需要重建索引:看 information_schema.INNODB_INDEX_STATS 和 SHOW INDEX
别凭感觉——先验证。重点查两处:
-
SELECT * FROM information_schema.INNODB_INDEX_STATS WHERE table_name = 'your_table' AND database_name = 'your_db';—— 关注n_diff_pfx01(唯一值估算)是否远低于实际,n_leaf_pages是否异常高(说明空洞多) -
SHOW INDEX FROM your_table;—— 对比Cardinality列与实际唯一值数量;若Cardinality接近 0 或恒为 1,且表有主键/唯一索引,大概率统计已过期 - 执行
ANALYZE TABLE your_table;后再查,若Cardinality无明显变化,说明采样不足或数据分布极端,需强制更新统计
重建索引的三种方式:何时用 OPTIMIZE TABLE,何时用 ALTER TABLE ... FORCE,何时必须 DROP + ADD
三者都触发重建,但行为和适用场景不同:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
OPTIMIZE TABLE your_table;:对 InnoDB 表等价于ALTER TABLE ... FORCE+ 更新统计;会锁表(8.0+ 可在线,但仍有 DML 阻塞窗口),适合中小表( -
ALTER TABLE your_table ENGINE=InnoDB;或ALTER TABLE your_table FORCE;:显式重建聚簇索引,释放空洞页,重排数据物理顺序;8.0 支持ALGORITHM=INPLACE(默认),但仍需额外磁盘空间(≈原表大小) -
DROP INDEX idx_name ON your_table; ADD INDEX idx_name (col);:仅重建单个二级索引,不影响聚簇索引;适合某索引严重退化但主键/聚簇结构尚可时;注意:DROP瞬间释放空间,ADD期间该列无法走索引
避免重复踩坑:重建不是万能解,关键在删除策略本身
频繁重建索引治标不治本。真正要控制的是删除方式:
- 禁用
DELETE FROM t WHERE ...批量删千万级数据——改用分批 +LIMIT+ 延迟提交,例如:DELETE FROM t WHERE id BETWEEN ? AND ? LIMIT 10000;,每批后SLEEP(0.1)减压 - 若删除比例 >30%,优先考虑
CREATE TABLE new_t AS SELECT ... FROM old_t WHERE keep_condition;+RENAME切换,比DELETE+OPTIMIZE更快更干净 - 定期(如每周)跑
ANALYZE TABLE,但对超大表(>100GB)慎用默认采样;可通过SET GLOBAL innodb_stats_persistent_sample_pages = 200;提高采样精度(需innodb_stats_persistent=ON)
重建索引本身不难,难的是判断“到底要不要建”以及“删的时候就别让它碎”。物理碎片和统计偏差往往并存,但修复顺序一定是:先确认统计是否失真 → 再决定是否重建 → 最后回溯删除逻辑是否合理。










