delete后.ibd文件不缩容是innodb设计行为,因其仅逻辑标记删除行、更新空闲链表,物理页仍保留在文件中供复用;必须通过optimize table或alter table engine=innodb重建表才能真正释放os级磁盘空间。

DELETE 不会减小 .ibd 文件大小,这是 InnoDB 的设计行为,不是 bug,也不是配置错误。
为什么 DELETE 后 .ibd 文件纹丝不动
InnoDB 的 DELETE 只是把行头的 delete_mask 位设为 1,并更新页内空闲链表,物理页仍保留在文件里。这些页后续可被新 INSERT 复用,所以:
• du -sh table.ibd 不变
• SHOW TABLE STATUS 中的 Data_length 不降
• 即使 SELECT COUNT(*) = 0,文件尺寸也完全不变
• Data_free 值反而可能变大——这不是泄漏,而是标记出更多“待复用但未归还 OS”的空间
什么情况下 OPTIMIZE TABLE 才真正有效
它本质是重建表:新建空表 → 逐行拷贝有效数据 → 替换原 .ibd 文件。只有这时旧页才被丢弃,OS 层面回收空间。但必须满足三个前提:
• innodb_file_per_table = ON(否则所有表共用 ibdata1,删再多也不缩)
• 表引擎确实是 ENGINE=InnoDB(SHOW CREATE TABLE t\G 确认)
• 没有长事务阻塞 purge 线程(查 information_schema.INNODB_TRX,运行超 600 秒的得先 KILL)
执行前不检查磁盘空间,大概率失败
OPTIMIZE TABLE 或 ALTER TABLE t ENGINE=InnoDB 需要额外磁盘空间 ≥ 当前表的 Data_length:
• df -h /var/lib/mysql 必须留出至少 1.5 倍冗余空间
• 临时表写满直接报 ERROR 1034,操作中断且锁未释放
• RDS 等托管服务可能禁用该命令,或只在只读副本上执行
• 主从复制中它不走 binlog(ROW 格式),会导致主从 .ibd 大小不一致
别以为 TRUNCATE TABLE 万能
TRUNCATE TABLE 会删掉原 .ibd 文件、新建空表,立刻释放空间,但它有硬限制:
• 不支持 WHERE 条件,只能整表清空
• 在有外键引用的子表上直接执行会报 ERROR 1701
• 不走 binlog row 事件,可能跳过审计逻辑
• MySQL 8.0+ 若 innodb_file_per_table = OFF,仍无法缩容系统表空间
真正容易被忽略的是:Data_free 高 ≠ 必须优化——它本就是为写入性能预留的缓冲空间;重建表的关键,从来不是“碎片多不多”,而是“你是否真的需要 OS 层面立刻回收那块磁盘”。











