DELETE大字段后磁盘空间不释放是InnoDB正常行为,因其将大对象存于溢出页且不回收;必须通过OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB重建表才能释放,前提是innodb_file_per_table=ON且无长事务阻塞purge。

DELETE 大字段(如 TEXT、MEDIUMTEXT、LONGBLOB)后磁盘空间不释放,不是字段没删干净,而是 InnoDB 对大对象(LOB)的存储机制和普通行数据不同——它根本不会把 LOB 页归还给操作系统,哪怕整行都被标记删除。
为什么 DELETE 不动 LOB 页物理空间
InnoDB 把大字段单独存放在溢出页(overflow page),主记录只存 20 字节的指针。执行 DELETE 时:
- 主记录行被标记 delete_mask = 1,指针失效
- 但溢出页本身仍保留在 .ibd 文件里,既不清理也不回收
- 这些页不会加入可复用链表,也不会被 purge 线程释放
- 所以 du -sh table.ibd、SHOW TABLE STATUS 的 Data_length 完全不变
OPTIMIZE TABLE 能否真正清理 LOB 页
能,但有条件:
- 必须满足 innodb_file_per_table = ON(否则所有 LOB 页都在 ibdata1,永远不释放)
- OPTIMIZE TABLE 或 ALTER TABLE t ENGINE=InnoDB 会重建整张表,包括重新分配所有 LOB 页
- 新建的 .ibd 文件只包含当前有效数据的 LOB 页,旧溢出页被丢弃,OS 层面回收空间
- 注意:重建过程需额外 ≥ 当前 Data_length 的磁盘空间,且期间 DML 全部阻塞
大字段删完后 Data_free 反而变小?
这是典型误判信号,原因有二:
- Data_free 统计的是“聚簇索引页内空闲字节”,不包含 LOB 溢出页空间
- 删除大量 LOB 行后,主记录页可能更紧凑,反而减少页内碎片,Data_free 下降
- 真实碎片在溢出段,Data_free 完全不反映它
不锁表的前提下清理 LOB 空间
纯 SQL 无解,但可绕过:
- 若表按时间分区(如 PARTITION BY RANGE (create_time)),直接 DROP PARTITION p2023 —— 整个分区的 LOB 页文件被 OS 立即回收
- 用 pt-online-schema-change --alter="ENGINE=InnoDB" 在线重建,要求磁盘余量 ≥ 原表大小
- mysqldump --where 导出有效行 + DROP + CREATE + INSERT,但存在数据窗口期,且 LOB 导出/导入性能差
真正容易被忽略的是:purge 线程卡住时(比如有运行超 10 分钟的事务),即使跑完 OPTIMIZE TABLE,LOB 页释放量也可能几乎为零——得先查 SHOW ENGINE INNODB STATUS\G 里的 History list length,超过 1000 就得先 kill 长事务。

















