DELETE后磁盘空间不释放是InnoDB正常设计行为,因仅标记删除而不物理回收;OPTIMIZE TABLE可收缩.ibd文件,但须满足innodb_file_per_table=ON、无长事务、磁盘空间充足三个前提。
DELETE 后磁盘空间不释放,是 InnoDB 的设计行为,不是故障;OPTIMIZE TABLE 能收缩 .ibd 文件,但必须满足三个硬性前提,否则会卡住、锁死或写满磁盘。
为什么 DELETE 不释放空间
InnoDB 的 DELETE 只是把行标记为“可复用”,并不归还给操作系统。数据文件(如
)大小完全不变,
中的
和
都不会下降。这不是配置错,也不是 bug——它是 MVCC 和 purge 机制共同决定的:未提交的长事务会阻塞 purge 线程,已删记录就一直占着页。
常见错误现象包括:
返回 0,但
反而变大(碎片加剧);
看不到磁盘释放;
能立刻缩容,但
完全无效。
OPTIMIZE TABLE 在 MySQL 5.7 上的实际行为
MySQL 5.7 的
本质是
,它重建整张表:读出所有有效行 → 写入新页 → 替换原
文件。因此它能真正释放空间,但代价明确:
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
全程加
锁,所有 DML 和大部分 SELECT 都被阻塞
需要额外磁盘空间 ≥ 当前表的
(临时表阶段写满就报
)
不走 binlog(ROW 格式下),从库不会同步该操作,主从
大小会不一致
即使
,若表本身已很紧凑(比如刚 INSERT 就 DELETE),
后
大小也可能不变
执行前必须检查的三件事
跳过任意一项,
就可能卡死或引发事故:
查磁盘余量:
,确认剩余空间 >
中的
查长事务:
,有结果就得先 kill
确认引擎和独立表空间:
必须含
且实例已启用
替代方案:什么情况下不该用 OPTIMIZE TABLE
当表超过 50GB 或磁盘使用率 >85% 时,
风险极高。更稳妥的做法是:
整表清空且允许停机:直接
(注意外键约束会报
)
只保留近期数据:用
导出,再
+
+
线上大表无法停机:改用
,但它仍需双倍磁盘空间,且不能在从库上运行
最易被忽略的一点:
不是“越勤快越好”。频繁执行反而会因临时表开销增大
,让碎片问题更隐蔽。重点应放在预防——比如避免超长事务、按月分区归档、监控
比值是否持续 > 25%。
test.ibdSHOW TABLE STATUSData_lengthdu -sh *.ibdSELECT COUNT(*)Data_freedf -hTRUNCATE TABLEDELETE FROM t WHERE ...OPTIMIZE TABLEALTER TABLE t ENGINE=InnoDB.ibdMDL_EXCLUSIVEData_lengthERROR 1034.ibdinnodb_file_per_table=ONOPTIMIZE.ibdOPTIMIZE TABLEdf -h /var/lib/mysqlSHOW TABLE STATUS LIKE 't'\GData_lengthSELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;SHOW CREATE TABLE t\GENGINE=InnoDBinnodb_file_per_table=ONOPTIMIZE TABLETRUNCATE TABLE tERROR 1701mysqldump -u root -p --where="create_time > '2025-01-01'" db t > t_recent.sqlDROPCREATEsourcept-online-schema-change --alter="ENGINE=InnoDB" --executeOPTIMIZE TABLEData_freeData_free / Data_length