DELETE不释放磁盘空间是InnoDB设计使然:仅逻辑标记删除、不物理回收页,空洞需通过OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB重建表才能清理;data_free>100MB或超Data_length的20%即属严重空洞。

DELETE 不会释放磁盘空间,空洞必须显式重建表才能清理。 这不是 bug,而是 InnoDB 的设计选择:标记删除比物理擦除更轻量,但代价是空间不自动返还 OS。生产环境里,DELETE 后 data_free 持续增长、SELECT COUNT(*) 变慢、SHOW TABLE STATUS 显示 Data_length 居高不下,基本可以断定是空洞在起作用。
怎么确认表存在严重空洞
空洞大小直接体现在 data_free 字段上,它代表当前表中“被标记删除但尚未复用”的空间总量:
- 执行
SHOW TABLE STATUS FROM `your_db` LIKE 'your_table'; - 检查返回结果中的
Data_length(实际数据占用)和Data_free(空洞大小) - 如果
Data_free> 100MB 或超过Data_length的 20%,说明空洞已影响显著 - 注意:
data_free对 MyISAM 表无意义,仅对 InnoDB 有效
OPTIMIZE TABLE 是最直接的修复方式
OPTIMIZE TABLE 在 MySQL 5.6+ 默认走 Online DDL 流程,本质是重建表(ALTER TABLE ... ENGINE=InnoDB + ANALYZE TABLE),能真正释放空间并整理 B+ 树结构:
- 它会创建新临时表,把旧表有效数据逐行拷贝过去,跳过所有被标记删除的记录
- 新表的 B+ 树页是紧凑填充的,
data_free归零,Data_length显著下降 - 执行期间允许读写(DML),但会加短暂元数据锁(MDL),大表需评估业务窗口
- 不要在从库上直接跑;如果主从延迟敏感,优先在低峰期操作
- 示例:
OPTIMIZE TABLE weixin_push_log_test;
ALTER TABLE ... ENGINE=InnoDB 更可控,适合大表或定制场景
相比 OPTIMIZE TABLE,手动触发重建更透明,也便于加参数控制行为:
- 等价于
OPTIMIZE TABLE的核心动作,但不附带ANALYZE步骤(索引统计需单独执行) - 可配合
ALGORITHM=INPLACE(MySQL 5.6+ 默认)或ALGORITHM=COPY显式指定方式 - 支持
LOCK=NONE(仅限部分操作,如加列不锁写);但重建表本身仍需短暂写锁 - 若表有外键依赖,需先禁用外键检查:
SET FOREIGN_KEY_CHECKS = 0;,操作完再恢复 - 示例:
ALTER TABLE your_table ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=SHARED;
哪些情况不能靠空洞自动复用,必须人工干预
空洞能否被后续 INSERT 复用,高度依赖写入模式。以下场景下,空洞大概率长期闲置:
- 主键为自增整型(
AUTO_INCREMENT),且新插入 ID 持续递增 → 新数据总写入末尾页,旧空洞永远等不到匹配的插入 - 表含大字段(
BLOB/TEXT),且删除后插入的新记录长度远小于原记录 → 页内剩余空间无法容纳新行 - 长期只删不插,或插入频率极低 → 空洞链表持续积累,Purge 线程只清理标记,不合并页
- 使用
ROW_FORMAT=COMPACT或DYNAMIC时,大字段外存策略会让空洞更难被跨页复用
空洞本身不破坏数据一致性,但它让磁盘 IO 和缓冲池效率持续劣化——尤其当 data_free 超过几个 GB 时,一次全表扫描可能多触发数万次随机 IO。重建不是“可选项”,而是容量与性能失衡后的必要收敛动作。


















