MySQL大规模删除后磁盘空间不释放是InnoDB正常设计行为,因DELETE仅逻辑标记行并加入可复用链表,不归还页给操作系统;必须通过OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB重建表才能真正缩容.ibd文件。

MySQL大规模删除后磁盘空间不释放,不是 bug,也不是配置错,而是 InnoDB 引擎的默认行为——DELETE 只做逻辑标记,不归还页(Page)给操作系统。
为什么 DELETE 不缩 .ibd 文件
InnoDB 把每行数据删掉时,只是在对应页里打上 deleted 标记,并把该行加入“可复用链表”。后续 INSERT 会优先填这些空位,所以 Data_length、du -sh table.ibd 都不会变小。哪怕 SELECT COUNT(*) = 0,只要没重建表,文件尺寸就纹丝不动。
-
innodb_file_per_table = OFF时,所有表共用ibdata1,删再多也永远不缩 —— 这个参数必须为ON才有释放可能 - Purge 线程只清理 undo 日志和行 slot,不把页还给 OS
-
TRUNCATE或DROP才会立即释放文件级空间;DELETE永远不等于清空磁盘
OPTIMIZE TABLE 实际做了什么
它不是“整理碎片”的魔法命令,而是触发一次完整重建:CREATE TABLE new AS SELECT * FROM old + DROP old + RENAME new → old。所有数据重写进新页,旧页丢弃,OS 层面才真正归还空间。
- MySQL 8.0 默认走
ALGORITHM=INPLACE,但依然要加 DDL 锁,大表可能卡住元数据锁(metadata lock) -
Data_free值很大 ≠ 能缩容;如果表刚批量插入又全删,重建后文件大小可能几乎不变 - 别在从库上直接跑 —— 它不走 binlog,主从
.ibd文件大小会不一致 - 重建操作本身会短暂占用双倍磁盘空间
线上大表不能停,还能怎么释放空间
纯 SQL 没有“在线 shrink .ibd”的能力。InnoDB 不支持动态收缩数据文件,这是引擎层硬限制。
-
pt-online-schema-change是目前最稳妥的方案:边复制边同步,最后原子切换,业务无感知,但要求磁盘剩余空间 ≥ 表原始大小 -
mysqldump + DROP + CREATE + INSERT可行,但耗时长、IO 压力大,且中间存在数据窗口期 -
ALTER TABLE t ENGINE=InnoDB和OPTIMIZE TABLE效果等价,别以为换写法就能绕过锁表 - 压缩表(
ROW_FORMAT=COMPRESSED)只影响新写入数据密度,对已有.ibd文件大小 零影响
最容易被忽略的是:purge 被阻塞时(比如有长事务未提交),OPTIMIZE TABLE 即使跑完,实际释放量也可能远低于预期——得先看 SHOW ENGINE INNODB STATUS\G 里的历史列表长度。


















