核心动因是表存在大量已删除但未回收的磁盘空间;需先确认innodb_file_per_table=on,再结合data_free是否超data_length的20%~30%判断是否值得执行optimize table。

看 DATA_FREE 是否显著偏高
执行 OPTIMIZE TABLE 的核心动因,是表存在大量“空洞”——即已删除但未回收的磁盘空间。最直接的判断依据,就是查 information_schema.TABLES 中的 DATA_FREE 字段:
-
DATA_FREE表示该表中未被使用的空间(单位:字节),主要来自DELETE或UPDATE产生的碎片(尤其对InnoDB) - 若
DATA_FREE > 0且接近或超过DATA_LENGTH的 20%~30%,说明碎片已较严重,值得优化 - 注意:
DATA_FREE对innodb_file_per_table = OFF的场景无意义(所有表共用ibdata1,空间无法单独回收)
检查 innodb_file_per_table 是否启用
这是能否真正释放磁盘空间的前提。如果关闭,OPTIMIZE TABLE 只能重建表结构、整理页内碎片,但不会把空间还给操作系统:
- 运行
SHOW VARIABLES LIKE 'innodb_file_per_table';,确认返回值为ON - 若为
OFF,先改配置并重启 MySQL,再对新创建的表生效;旧表需ALTER TABLE t ENGINE=InnoDB;才能迁出ibdata1 - 不满足这个条件时,
OPTIMIZE TABLE效果极其有限,别白跑
观察 innodb_page_size 和频繁 UPDATE 场景
某些写入模式会让碎片快速积累,即使 DATA_FREE 暂时不高,也建议定期评估:
- 使用默认
innodb_page_size = 16K时,频繁更新变长字段(如VARCHAR超出原长度)会触发页分裂,产生内部碎片 - 日志型表(如订单状态变更表)、计数器表(频繁
UPDATE ... SET cnt = cnt + 1)容易出现高DATA_FREE,建议每月查一次 -
SHOW TABLE STATUS LIKE 'table_name'中的Rows和Data_length / Avg_row_length差异过大(比如估算行数比实际多 2 倍以上),也是碎片信号
OPTIMIZE TABLE 在 ALGORITHM=INPLACE 下是否真轻量
MySQL 5.6+ 默认尝试 INPLACE 算法,但并非总成功。实际效果取决于存储引擎和操作类型:
-
InnoDB表执行OPTIMIZE TABLE本质是ALTER TABLE ... FORCE,会重建聚簇索引,期间需要额外磁盘空间(至少等于原表大小) - 若磁盘剩余空间不足,操作会失败并报错:
ERROR 1030 (HY000): Got error 28 from storage engine - 大表(>50GB)务必在低峰期执行,并监控
innodb_buffer_pool_pages_dirty和innodb_log_file_size配置是否足够支撑脏页刷盘节奏
碎片不是万能解药,盲目 OPTIMIZE TABLE 可能引发锁表、IO 尖峰甚至 OOM。真正要盯的是 DATA_FREE + innodb_file_per_table + 实际磁盘压力三者的组合信号。











