optimize table对innodb表本质是重建操作,等价于alter table t engine=innodb,通过创建新表、拷贝有效行、重建索引、原子替换实现碎片清理与空间释放。

直接结论:对 InnoDB 表清理碎片,优先用 OPTIMIZE TABLE;若需更彻底或规避锁表风险,可用 ALTER TABLE ... ENGINE=InnoDB,但两者本质都是重建表。
为什么 OPTIMIZE TABLE 在 MySQL 5.7 中对 InnoDB 是重建操作
MySQL 5.7 的 InnoDB 不支持“原地整理碎片”,OPTIMIZE TABLE t 实际被自动转换为 ALTER TABLE t ENGINE=InnoDB, ALGORITHM=COPY(即拷贝重建)。它会:创建新表、逐行拷贝数据、重建聚簇索引和所有二级索引、删除旧表文件。所以效果等同于显式重建引擎,但语法更简洁、语义更明确。
常见错误现象:OPTIMIZE TABLE 执行后 Data_free 没变?可能原因包括:表空间类型为 innodb_file_per_table=OFF(碎片空间无法返还给操作系统),或 innodb_file_format 过旧(但 5.7 默认为 Barracuda)。
- 确认是否启用独立表空间:
SHOW VARIABLES LIKE 'innodb_file_per_table';— 必须为ON才能释放磁盘空间 - 检查碎片大小:
SHOW TABLE STATUS LIKE 'your_table_name';关注Data_free字段(单位字节) - 执行前务必在低峰期操作,因为整个过程会锁表(
ALGORITHM=COPY阶段为写锁)
ALTER TABLE ... ENGINE=InnoDB 的实际行为与陷阱
这条命令在 MySQL 5.7 中也触发完整重建,但它比 OPTIMIZE TABLE 更“底层”:不走优化器路径,直接强制重定义存储引擎。好处是可配合 ALGORITHM=INPLACE(仅限某些 DDL 场景)尝试减少锁,但对纯碎片清理无效——InnoDB 引擎重建必须用 COPY 模式。
容易踩的坑:
- 误以为加了
ROW_FORMAT=COMPACT或KEY_BLOCK_SIZE就能跳过重建:不会,只要ENGINE=InnoDB出现,就触发重建 - 未检查磁盘剩余空间:重建过程需要额外 ≈ 1.5 倍原表空间,否则中途失败并回滚,表仍可用但无任何清理效果
- 主从环境中,该语句会完整复制到从库执行,可能造成从库延迟;建议先在从库停掉 SQL 线程再操作
真正影响碎片清理效果的三个隐藏配置
即使执行了重建,如果以下配置不合理,Data_free 可能依然很高,或很快复发:
-
innodb_fill_factor(默认 100):控制页内填充比例。设为 80~90 可预留更新空间,减少后续页分裂带来的碎片,但会略微增大总空间占用 -
innodb_page_cleaners(默认 4):影响脏页刷盘节奏,间接关系 Purge 线程能否及时回收已删记录的空间。高并发删除场景建议调高至 CPU 核数 -
innodb_purge_threads(默认 4):Purge 线程不足会导致标记删除的记录长期滞留,使Data_free虚高。可通过SHOW ENGINE INNODB STATUS查看PURGE PROCESSED是否滞后
这些参数不改变单次清理动作,但决定碎片是否“治标又治本”。
别忽略 information_schema.TABLES 的查询方式
批量识别碎片表不能只靠肉眼扫 SHOW TABLE STATUS。用以下查询快速定位:
SELECT
table_schema,
table_name,
round(data_free / 1024 / 1024, 2) AS data_free_mb,
round((data_length + index_length) / 1024 / 1024, 2) AS total_mb,
round(data_free / (data_length + index_length), 4) AS frag_ratio
FROM information_schema.TABLES
WHERE engine = 'InnoDB'
AND table_schema NOT IN ('information_schema', 'mysql', 'performance_schema')
AND data_free > 104857600 -- 大于 100MB 才纳入清理范围
ORDER BY data_free DESC;
注意:data_free 为 0 并不绝对代表无碎片(例如小表页内空洞无法统计),但大于 100MB 就值得干预。另外,frag_ratio 超过 0.3 通常意味着严重碎片化。
真正麻烦的不是执行命令,而是判断“值不值得清”——碎片率低但表极大时,重建耗时可能远超收益;碎片率高但访问极少,反而不该动。业务语义和访问模式,永远比 Data_free 数值更重要。











