innodb表碎片整理需谨慎,data_free高不等于必须optimize;真正需干预的是data_free/(data_length+index_length)>25%且缓冲池读取异常或执行计划失真;optimize本质是alter table engine=innodb,仅当innodb_file_per_table=on、无全文索引等限制时才可能收缩.ibd文件;更安全替代方案为alter table ... algorithm=inplace, lock=none或pt-online-schema-change。

确认是否真需要碎片整理
别一看到 DATA_FREE 就执行 OPTIMIZE TABLE。InnoDB 的 purge 线程会自动回收大部分已删除行的空间,DATA_FREE 高 ≠ 必须人工干预。真正该动手的信号是:DATA_FREE / (DATA_LENGTH + INDEX_LENGTH) 持续超过 25%,且观察到 innodb_buffer_pool_reads 明显上升、innodb_buffer_pool_read_requests 下降,或慢查询中 EXPLAIN 的 rows 严重偏离实际扫描量。
为什么 OPTIMIZE TABLE 对 InnoDB 常常没效果
OPTIMIZE TABLE 在 InnoDB 上本质是 ALTER TABLE ... ENGINE=InnoDB,但它能否缩小 .ibd 文件,取决于两个硬条件:
-
innodb_file_per_table=ON(默认开启,但需确认)——否则数据写在共享表空间ibdata1中,无法收缩物理文件 - 表必须是独立
.ibd文件,且没有全文索引、虚拟列、外键约束等限制项,否则 MySQL 8.0+ 会自动退化为ALGORITHM=COPY,全程锁表 - 即使满足条件,若碎片主要来自二级索引页内空洞(而非外部空闲页),
DATA_LENGTH可能几乎不变,磁盘空间也不释放
更安全、更可控的替代方案
优先用带明确 DDL 控制的语句,避免黑盒行为:
-
ALTER TABLE t1 ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE—— 在 MySQL 5.6+ 支持在线重建,但要求FILE_FORMAT=Barracuda(查information_schema.INNODB_TABLES),且不能有全文索引 - 对超大表(>50GB),用
pt-online-schema-change --alter="ENGINE=InnoDB" D=db,t=tbl --execute,全程无锁,但需额外部署 Percona Toolkit - 如果只是想释放空间给 OS,且允许短时停服,直接
mysqldump导出再导入,比OPTIMIZE更彻底,也绕过所有引擎层限制
容易被忽略的底层前提
所有碎片整理操作的前提是:你已经关掉了 innodb_file_per_table=OFF 这种旧配置;你清楚当前表的 ROW_FORMAT(COMPACT 或 DYNAMIC)和 FILE_FORMAT(Antelope 已不支持多数在线 DDL);并且你监控的不是单个 DATA_FREE,而是结合 sys.schema_table_statistics 查看索引页填充率——低于 70% 才说明 B+ 树内部真的“松垮”了。











