OPTIMIZE TABLE或ALTER TABLE ENGINE=InnoDB后.ibd文件不缩容,主因是InnoDB不主动归还空闲页给OS,且受未清理undo log、change buffer积压或innodb_file_per_table关闭制约。

执行 OPTIMIZE TABLE 或 ALTER TABLE ... ENGINE=InnoDB 后,.ibd 文件大小大概率不会立刻变小——这不是操作失败,而是 InnoDB 的空间回收机制有明确前提条件。
为什么 OPTIMIZE TABLE 没缩表文件?
它在 InnoDB 中实际等价于 ALTER TABLE ... FORCE,只做页内整理和逻辑重建,不主动归还空闲页给操作系统。常见假阳性现象:
-
SHOW TABLE STATUS显示Data_free很大,但ls -lh table.ibd大小纹丝不动 - 执行完后
INFORMATION_SCHEMA.INNODB_METRICS中buffer_pool_pages_free未明显上升 - 磁盘上
.ibd文件仍占满,innochecksum -v table.ibd | grep "fill factor"却显示页填充率 > 85%
真正卡住空间释放的,往往不是表数据本身,而是:
- 未清理的 undo log(存在长事务、XA 事务未提交、只读事务未退出)
- change buffer 积压(尤其在高写入后立即执行)
-
innodb_file_per_table关闭(此时表空间属于系统表空间ibdata1,根本无法单独收缩)
ALTER TABLE ... ENGINE=InnoDB 和 OPTIMIZE TABLE 有什么实质区别?
对 InnoDB 表而言,两者底层行为高度一致,但触发路径和锁行为细节不同:
-
OPTIMIZE TABLE在 MySQL 5.7+ 默认走ALGORITHM=INPLACE,但仍需两次 S 级 MDL 锁(开始和结束),中间允许 DML,但遇到冲突会排队等待 -
ALTER TABLE t ENGINE=InnoDB更“裸”,不带额外语义判断;若表含全文索引、虚拟列或外键,它不会自动退化,而OPTIMIZE TABLE会检测并强制切到COPY算法(全程 X 锁) - 二者都不重建二级索引定义(比如字段顺序、表达式),只重建物理结构;如需调整索引,必须显式用
DROP INDEX+ADD INDEX
性能影响上,两者都会大量读取旧页、重写新页,易导致 Buffer pool hit rate 短时骤降,连带拖慢其他查询。
什么情况下该换用 pt-online-schema-change?
当表大于 1GB、业务无法容忍任何 DML 阻塞、或已知存在全文索引/外键等退化风险时,pt-online-schema-change 是更可控的选择:
- 它通过 chunk 分批拷贝 + 触发器同步,把 I/O 和锁压力摊薄,避免单次大负载冲击 buffer pool
- 支持
--chunk-size和--sleep调节节奏,可配合监控动态调整 - 命令示例:
pt-online-schema-change --alter="ENGINE=InnoDB" D=your_db,t=your_table --execute - 注意:它要求主键或唯一非空索引存在,且不能有触发器(否则需加
--no-check-triggers并自行评估风险)
真正想让 .ibd 缩小,必须盯住这三件事
别只盯着表本身。碎片是否能落地释放,取决于全局状态:
- 确认没有活跃长事务:
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5; - 检查 XA 事务:
XA RECOVER;,如有输出需手动XA ROLLBACK或XA COMMIT - 观察 purge 进度:
SHOW ENGINE INNODB STATUS\G查看HISTORY LIST长度,若持续 > 1000 且不下降,说明 undo 清理被阻塞
做完重建后,别急着看文件大小——先等几分钟,再查 buffer_pool_pages_free 是否上升、ls -lh 是否变化。很多 case 是“操作已生效,只是空间还没腾出来”。


















