DELETE 不释放磁盘空间是 InnoDB 正常机制:仅标记删除、不物理擦除,Purge 线程异步清理行但不归还页给操作系统;需 OPTIMIZE TABLE 或 ALTER TABLE ENGINE=InnoDB 重建表才能真正释放空间。

DELETE 不释放磁盘空间,是 InnoDB 的正常行为,不是 bug,也不是配置错误。它背后是 Purge 线程、页级复用机制和表空间管理逻辑共同作用的结果——想靠删完就缩容,行不通。
为什么 DELETE 后 Data_length 完全不变?
InnoDB 删除行时,只是把记录打上“deleted”标记,并不立即擦除物理页,也不合并空页。这些被标记的记录会由后台 Purge 线程异步清理,但 Purge 只负责回收 undo 日志和释放行级空间,**不会把页还给操作系统**。
-
Purge线程真正干的事:从 undo log 中移除旧版本、把被删行的 slot 标记为空闲、允许后续 INSERT 复用该位置 - 页(Page)仍保留在 .ibd 文件中,
Data_length统计的是已分配页的总大小,不是“有效数据”大小 - 即使
SELECT COUNT(*) = 0,只要没重建表,文件尺寸就纹丝不动 - 若
innodb_file_per_table = OFF(共用ibdata1),删再多也永远不缩 —— 这个参数必须为ON才有释放可能
OPTIMIZE TABLE 真正做了什么?
它不是“整理碎片”的魔法命令,而是触发一次完整的表重建:CREATE TABLE new AS SELECT * FROM old + DROP old + RENAME new → old。所有数据重写进新页,旧页彻底丢弃,OS 层面才真正归还空间。
- 执行前务必确认
innodb_file_per_table = ON,否则重建后仍写入ibdata1,白忙一场 - 5.7+ 默认走
ALGORITHM=INPLACE,但仅对某些 DDL 有效;OPTIMIZE TABLE在 8.0 里仍会短暂加 DDL 锁,大表可能卡住元数据锁(metadata lock) -
Data_free值很大 ≠ 一定能缩容;如果表本身很稀疏(比如刚批量插入又全删),重建后文件大小可能几乎不变 - 别在从库上直接跑 —— 它不走 binlog,主从
.ibd文件大小会不一致
线上大表不能停,还能怎么释放空间?
纯 SQL 没有“在线收缩 .ibd”的能力。InnoDB 不支持动态 shrink 数据文件,这是引擎层硬限制。
-
pt-online-schema-change是目前最稳妥的方案:它边复制边同步,最后原子切换,业务无感知,但要求磁盘剩余空间 ≥ 表原始大小 - 导出导入(
mysqldump+mysql)可行,但耗时长、网络/IO 压力大,且中间存在数据窗口期 -
ALTER TABLE ... ENGINE=InnoDB和OPTIMIZE TABLE效果等价,别以为换写法就能绕过锁表 - 压缩表(
ROW_FORMAT=COMPRESSED)只影响新写入数据密度,对已有 .ibd 文件大小 **零影响**
最容易被忽略的三个点
一是 innodb_file_per_table 必须为 ON,且这个值在建库时就该定死,运行中改了也不生效;二是 OPTIMIZE TABLE 期间磁盘占用会短暂翻倍,别在只剩 10% 空间时硬上;三是 Purge 线程延迟高(比如 innodb_max_purge_lag 设得过大)会导致 delete 后 visible row count 下降慢,误判“删没成功”。











