DELETE后索引树大小不变,因InnoDB仅标记删除、不物理擦除,deleted_flag置1后记录仍占页空间,B+树节点不合并,空洞保留;需OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB重建聚簇索引才能真正回收空间。

为什么DELETE后索引树大小不变
InnoDB 的 DELETE 操作本质是标记删除,不是物理擦除。它只把记录的 deleted_flag 置为 1,并加入 purge 队列;B+ 树节点不会立即合并或收缩,页内空洞(gap)保留,但整页仍归属该表。只要还有活跃事务(比如长事务未提交)、或者 HISTORY LIST LENGTH 较高(SHOW ENGINE INNODB STATUS\G 查看),这些“已删”记录就得留着供 MVCC 回溯,索引树自然无法瘦身。
OPTIMIZE TABLE 能否真正回收空间
能,但有硬性前提:
-
innodb_file_per_table = ON必须启用(SHOW VARIABLES LIKE 'innodb_file_per_table'确认),否则数据锁在共享表空间ibdata1里,重建无效 - 执行前磁盘剩余空间 ≥ 当前
.ibd文件大小 × 1.2,否则重建中途失败会导致表不可用 -
data_free / (data_length + index_length) > 0.3(碎片率超 30%)才值得触发,否则性价比低 - 该命令会锁表(InnoDB 下是 ALGORITHM=COPY 模式),线上业务需避开高峰
ALTER TABLE ENGINE=InnoDB 和 OPTIMIZE TABLE 效果一样吗
效果完全一致,都是重建聚簇索引:
- 新建空
.ibd文件 - 扫描原表,只读取未被标记删除的有效行,按主键顺序写入新文件
- 原子替换原文件,同时完成空闲页回收、B+ 树重构、碎片合并
- 二者都依赖
innodb_file_per_table = ON,且同样需要充足磁盘空间
区别仅在于语义:OPTIMIZE TABLE 更明确表达“优化意图”,而 ALTER TABLE ... ENGINE=InnoDB 是显式重指定引擎——实际执行逻辑相同。
分区表 DROP PARTITION 是唯一秒级释放方案
如果你的表已按时间/范围分区(如 PARTITION BY RANGE (created_at)),这是最干净的释放方式:
-
ALTER TABLE t DROP PARTITION p202401直接从文件系统删除对应分区的.ibd文件,空间秒还操作系统 - 无需额外磁盘空间,不锁全表,只影响目标分区
- 但前提是建表时就定义了分区,事后加分区需
REORGANIZE,仍触发全量拷贝 - 同样要求
innodb_file_per_table = ON,否则分区数据也混在ibdata1中,删了白删
真正释放空间的关键不在“删得多”,而在“删得准”——要么重建索引树,要么直接扔掉整个物理文件。其他所有中间手段(比如只跑 purge 或等后台清理)都无法绕过 InnoDB 的页级复用机制。


















