delete不释放磁盘空间是innodb正常行为,因其仅逻辑标记删除行、不物理回收页空间;必须通过optimize table或alter table engine=innodb重建表才能真正缩容.ibd文件。

DELETE 不会释放磁盘空间,这是 InnoDB 的正常行为,不是 bug。真正释放物理空间必须重建表结构。
为什么 DELETE 后 ibd 文件大小不变
InnoDB 把数据存放在固定大小的页面(page)里。DELETE 只是把行标记为“已删除”,页面本身仍保留在 .ibd 文件中,供后续 INSERT 复用。操作系统看不到这部分空间可回收,du -h table.ibd 自然不变。
常见误判现象:
-
SHOW TABLE STATUS LIKE 't1'中Data_free显示几百 MB,但磁盘文件没缩 - 删掉 90% 数据后,
SELECT查询变慢,EXPLAIN显示扫描行数暴增 - 表里大量
VARCHAR/TEXT字段,且经历过高频UPDATE/DELETE
OPTIMIZE TABLE 真实做了什么,以及它的问题
OPTIMIZE TABLE t1 在 InnoDB 中本质是隐式执行 ALTER TABLE t1 ENGINE=InnoDB,流程是:
- 新建临时
.ibd文件(如#sql-ib123-456789.ibd) - 按主键顺序读原表、写新文件(重建所有索引)
- 原子交换文件名,删旧文件
但它有硬伤:
- 默认加 MDL 写锁:允许读,**阻塞所有
INSERT/UPDATE/DELETE** - 需要**双倍磁盘空间**:新旧
.ibd同时存在,直到完成 - 百 GB 表可能跑数小时,期间从库也要重放 DDL,拖慢复制
- MySQL 8.0+ 也**不自动启用
ALGORITHM=INPLACE**,除非你显式指定
更安全的替代操作:ALTER TABLE ... ALGORITHM=INPLACE
显式重建比 OPTIMIZE TABLE 更可控,尤其在 MySQL 8.0+:
- 必须满足前提:无全文索引、无虚拟列、
ROW_FORMAT兼容(比如都是DYNAMIC) - 执行前先查:
SHOW CREATE TABLE t1,确认ROW_FORMAT和KEY_BLOCK_SIZE - 命令示例:
ALTER TABLE t1 ENGINE=InnoDB ALGORITHM=INPLACE LOCK=NONE; - 失败会退化为
COPY模式(即锁表),所以务必提前验证条件
如果表太大或条件不满足,宁可分批 DELETE LIMIT 10000 + SLEEP(0.1),也别强上全量重建。
日常预防比事后补救更重要
空洞是攒出来的,不是删出来的。关键在源头控制:
-
innodb_file_per_table=ON必须开启(MySQL 5.6.6+ 默认已开),否则删整表都释放不了空间 - 大批量清空优先用
TRUNCATE TABLE t1:立刻释放空间、不走 binlog、不锁行(但会锁整表,且不可回滚) - 监控碎片率:
SELECT TABLE_NAME, DATA_LENGTH, DATA_FREE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='db' AND TABLE_NAME='t1';,当DATA_FREE > DATA_LENGTH * 0.25才值得动手 - 避免在高并发场景下对大表执行单次百万级
DELETE,容易卡死、撑爆 undo log
真正要警惕的不是“删了没缩容”,而是没看 DATA_FREE 就盲目跑 OPTIMIZE TABLE——那不是回收空间,是制造停机风险。











