optimize table仅在innodb_file_per_table=on、已删大量数据且无长事务时才有效;判断碎片需查information_schema.tables.data_free字段,>100mb或碎片率>30%才值得执行,否则基本无效。

不能一上来就跑 OPTIMIZE TABLE,它既不自动触发,也不适合高频执行;真正起效的前提是:表用的是独立表空间(innodb_file_per_table=ON)、已删除大量数据、且当前无长事务阻塞。
怎么判断一张表真有碎片可回收
别看磁盘上 .ibd 文件大就动手。关键字段是 information_schema.TABLES.DATA_FREE——它不是“空闲空间”,而是 InnoDB 预估的内部碎片字节数,只对独立表空间有效。
- 查碎片 >100MB 的表:
SELECT TABLE_SCHEMA, TABLE_NAME, sys.FORMAT_BYTES(DATA_FREE) AS fragment FROM information_schema.tables WHERE DATA_FREE > 100*1024*1024 AND ENGINE='InnoDB'; - 碎片率 >30% 更值得关注:
(DATA_FREE / (DATA_LENGTH + INDEX_LENGTH)) > 0.3 - 如果
DATA_FREE是 0 或几 KB,OPTIMIZE TABLE基本不会缩小文件 -
innodb_file_per_table=OFF时,DATA_FREE恒为 0,但真实碎片仍在ibdata1里,OPTIMIZE TABLE完全无效
OPTIMIZE TABLE 执行失败或卡住的常见原因
报错 “Table does not support optimize…” 或长时间无响应,大概率不是语法问题,而是环境不满足:
-
SELECT @@innodb_file_per_table;返回 0?那必须先开启参数并重建表,否则所有 InnoDB 表都无法收缩 -
SHOW PROCESSLIST;中状态为Waiting for table flush?说明有长事务(比如未提交的BEGIN、慢查询、或应用端连接泄漏)在 hold 表 - 磁盘空间不足:重建过程会先写新
.ibd,再原子替换旧文件,需预留至少等量临时空间 - RDS 等托管服务可能限制该命令,或需走控制台工单流程
替代方案:ALTER TABLE ENGINE = InnoDB 和重建陷阱
当 OPTIMIZE TABLE 不可用或想更可控时,ALTER TABLE tbl_name ENGINE = InnoDB 是等效操作——本质就是重建表。但它有隐含风险:
- MySQL 8.0+ 默认加
ALGORITHM=INPLACE,但大表仍可能退化为COPY,全程锁表 - 执行后
AUTO_INCREMENT值会被重置为当前最大值 +1,若业务依赖连续 ID,可能出问题 - 索引统计信息会更新,但某些旧版本 MySQL 可能不自动
ANALYZE TABLE,需手动补上 - 不要用
CREATE TABLE new LIKE old; INSERT ...; DROP; RENAME这套手动流程——没事务保障,中间失败易丢数据
最易被忽略的一点:碎片清理不是“越勤越好”。官方明确建议按周或月执行,而非每天甚至每小时。频繁重建大表不仅加重 I/O 压力,还可能因锁表时间不可控,反向冲击业务 SLA。真正该盯紧的,是那些高频删改、又长期没维护的订单、日志、监控类表——它们才是碎片主力。











