索引不会因delete失效,但会产生空洞页和统计失真,导致查询变慢、执行计划错误;可通过information_schema.innodb_index_stats和show index检查n_leaf_pages、n_diff_pfx01及cardinality判断,执行analyze table后若cardinality无变化需强制重建。

大量 DELETE 不会让索引“失效”,但会留下大量空洞页、让统计信息失真,最终导致查询变慢、执行计划出错——这不是 bug,是 InnoDB 的正常行为。
怎么确认索引已经因删除出现空洞和统计失真?
别猜,查 INFORMATION_SCHEMA 和 SHOW INDEX:
-
SELECT * FROM information_schema.INNODB_INDEX_STATS WHERE table_name = 'your_table' AND database_name = 'your_db';—— 关注n_leaf_pages(叶子页数)是否异常高,n_diff_pfx01(首列唯一值估算)是否远低于实际 -
SHOW INDEX FROM your_table;—— 看Cardinality列:若接近 0 或恒为 1,而表明明有主键/唯一索引,基本可断定统计过期 - 执行
ANALYZE TABLE your_table;后再查,如果Cardinality无变化,说明采样不足或数据分布极端(比如某字段 99% 是 NULL),需强制干预
重建索引该选 OPTIMIZE TABLE、ALTER TABLE FORCE 还是 DROP + ADD?
三者都重建索引,但触发机制、影响范围、适用场景完全不同:
-
OPTIMIZE TABLE your_table;:InnoDB 下等价于ALTER TABLE ... FORCE+ 更新统计;8.0+ 支持在线,但仍会短暂阻塞 DML;适合中小表( -
ALTER TABLE your_table ENGINE=InnoDB;或ALTER TABLE your_table FORCE;:显式重建聚簇索引,释放空洞页、重排物理顺序;需额外磁盘空间(≈原表大小);适合聚簇结构已严重碎片化的情况 -
DROP INDEX idx_name ON your_table; ADD INDEX idx_name (col);:仅重建单个二级索引,不影响聚簇索引;DROP瞬间释放空间,但ADD期间该列无法走索引;适合某二级索引退化严重,而主键结构尚可时
为什么重建后不久又变慢?根本问题在删除方式
频繁重建索引是补救,不是解法。真正要控制的是 DELETE 本身:
- 禁用
DELETE FROM t WHERE ...一次性删百万级——改用带LIMIT的分批:DELETE FROM t WHERE id BETWEEN ? AND ? LIMIT 10000;,每批后加SLEEP(0.1)缓压 - 若删除比例 >30%,优先用
CREATE TABLE t_new AS SELECT * FROM t WHERE keep_condition;+RENAME切换;比DELETE+OPTIMIZE更快、更干净 - 确保
WHERE条件走索引:避免函数(如DATE(create_time))、隐式类型转换、非最左前缀匹配;否则仍是全表扫描 + 行锁升级 - 检查
innodb_file_per_table是否为ON:关掉的话,空洞空间永远无法返还 OS;开的话,OPTIMIZE或重建后.ibd文件才可能收缩
空洞不是立即可见的磁盘浪费,而是缓慢积累的执行计划劣化源;重建索引只是“擦黑板”,而没改掉“乱写板书”的人——那个在业务逻辑里随手写 DELETE ... WHERE status = 'old' 却不加索引、不分批、不评估比例的人。











