DELETE不释放磁盘空间是正常现象,因InnoDB仅标记行为空闲;OPTIMIZE TABLE可回收但代价高;推荐ALTER TABLE ... ENGINE=InnoDB配合ALGORITHM=INPLACE;优先通过innodb_file_per_table、TRUNCATE或分批删除预防空洞。

DELETE 后磁盘空间不释放是正常现象
MySQL 的 DELETE 语句只是标记数据行为空闲,不会立即归还磁盘空间给操作系统。尤其是使用 InnoDB 引擎时,删除产生的“空洞”会保留在表空间文件(ibd)中,供后续 INSERT 复用。这意味着你删了 90% 的数据,du -h table.ibd 看到的文件大小几乎不变。
OPTIMIZE TABLE 能回收空间,但代价很高
OPTIMIZE TABLE 对 InnoDB 表实际执行的是 ALTER TABLE ... REBUILD:新建一个紧凑的表结构,把有效数据拷过去,再删掉旧文件。它确实能缩小 .ibd 文件、减少碎片,但有明显副作用:
- 执行期间会加 全表独占锁(MySQL 5.6+ 支持 Online DDL 的部分操作,但
OPTIMIZE TABLE默认仍会阻塞写入) - 需要**双倍磁盘空间**:新旧表同时存在,直到重建完成
- 耗时与表大小强相关,百 GB 级别可能持续数小时
- 在主从架构下,该操作会以 DDL 形式写入 binlog,从库同样要重放,可能拖慢复制
更安全的替代方案:ALTER TABLE ... ENGINE=InnoDB
这个命令效果和 OPTIMIZE TABLE 类似,但行为更可控:
- 显式指定引擎,避免隐式行为歧义
- 可在 MySQL 8.0+ 中配合
ALGORITHM=INPLACE和LOCK=NONE实现真正在线重建(需满足条件:无全文索引、无虚拟列、行格式兼容等) - 示例:
ALTER TABLE t1 ENGINE=InnoDB ALGORITHM=INPLACE LOCK=NONE;
- 执行前务必检查
SHOW CREATE TABLE t1中的ROW_FORMAT和KEY_BLOCK_SIZE,避免因参数不兼容退化为 COPY 模式(即锁表)
日常应优先靠配置预防,而非事后清理
与其频繁 OPTIMIZE,不如从源头减少空洞产生:
- 用
innodb_file_per_table=ON(默认已开启),确保每个表独立.ibd文件,删除整表时能直接释放空间 - 对大批量删除场景,改用
TRUNCATE TABLE(不能回滚、不走 binlog、立刻释放空间),或分批DELETE ... LIMIT 10000配合SLEEP(0.1)减轻锁竞争 - 监控
DATA_FREE字段:SELECT TABLE_NAME, DATA_LENGTH, DATA_FREE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='db_name' AND TABLE_NAME='t1';
当DATA_FREE显著大于DATA_LENGTH的 20%,才值得干预
真正要回收物理空间时,最常被忽略的是:没确认是否启用了 innodb_file_per_table,结果对系统表空间里的表执行 OPTIMIZE,完全无效。











