InnoDB的DELETE仅逻辑标记删除,不释放磁盘空间;真正释放需OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB重建表,重写有效数据并删除旧.ibd文件。

DELETE 不会释放磁盘空间,这是 InnoDB 的正常行为,不是 bug、配置错误或磁盘故障——它压根就不把页还给操作系统。
为什么 DELETE 后 du -sh table.ibd 完全不变?
InnoDB 的 DELETE 是逻辑删除:仅把行头的 delete_mask 位设为 1,并更新页内空闲链表。整页(16KB)仍保留在 .ibd 文件里,供后续 INSERT 复用。文件大小由操作系统分配,InnoDB 不会主动截断或 shrink。
常见误判现象包括:
-
SELECT COUNT(*) = 0,但Data_length和du -sh *.ibd均无变化 -
Data_free反而增大——这不是泄漏,是标记出更多“待复用但未归还 OS”的页 - 即使
DELETE FROM t WHERE 1=1清空全表,.ibd尺寸纹丝不动
OPTIMIZE TABLE 真正做了什么?
它不是“整理碎片”的魔法命令,而是完整重建表:CREATE TABLE new AS SELECT * FROM old → DROP old → RENAME new → old。所有有效行重写进新页,旧 .ibd 被删除,OS 才真正回收空间。
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
关键事实:
- MySQL 5.7+ 中
OPTIMIZE TABLE t等价于ALTER TABLE t ENGINE=InnoDB,且默认走ALGORITHM=INPLACE(仍有锁) - 不写 binlog(ROW 格式下),主从
.ibd文件大小会不一致 - 执行后
Data_free归零,.ibd回落到实际数据 + 索引占用水平
执行前必须检查的三件事,缺一不可
跳过任意一项,OPTIMIZE TABLE 很可能卡住、锁死业务,甚至写满磁盘报 ERROR 1034:
- 查磁盘余量:
df -h /var/lib/mysql,确认剩余空间 ≥SHOW TABLE STATUS LIKE 't'\G中的Data_length - 查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60,有结果必须先KILL - 确认引擎和独立表空间:
SHOW CREATE TABLE t\G必须含ENGINE=InnoDB且实例已启用innodb_file_per_table=ON
什么情况下不该用 OPTIMIZE TABLE?
它适合中等规模、碎片明显(Data_free > 1GB)、且能接受短时锁表的场景。但以下情况风险极高:
- 表体积 > 50GB 或磁盘使用率 > 85%,临时空间极易耗尽
- 业务无法停写:全表加
MDL_EXCLUSIVE锁,所有 DML 和大部分SELECT都被阻塞 - 只想保留近期数据:用
mysqldump --where="create_time > '2025-01-01'"导出再重建更可控 - 线上大表不能停机:改用
pt-online-schema-change --alter="ENGINE=InnoDB",但它仍需双倍磁盘空间
最易被忽略的一点:即使满足全部前提,若表本身刚插入就大量删除(比如日志表按天轮转),OPTIMIZE TABLE 后 .ibd 大小也可能完全不变——因为数据本就紧凑,没多少可合并的空洞。

















