optimize table 实际上是重建表而非修复碎片,它抛弃旧结构、按主键重写数据至新.ibd文件,清空页内空闲空间并重建二级索引;在mysql 8.0+中默认等价于alter table t engine=innodb, algorithm=inplace, lock=none(满足条件时),否则退化为copy全表锁。

OPTIMIZE TABLE 实际上是重建表,不是“修复”碎片
它不修补现有页里的空洞,而是抛弃旧结构、从头建一张新表。对 InnoDB 来说,OPTIMIZE TABLE t 在 MySQL 8.0+ 默认等价于:ALTER TABLE t ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE(满足条件时);不满足则退化为 ALGORITHM=COPY,触发全表锁。
这个过程强制把所有数据按主键顺序重写进新 .ibd 文件,所有二级索引也同步重建——页内空闲空间被清空,页间物理连续性恢复,B+ 树高度可能降低,统计信息也被刷新。
所以它“解决”碎片,靠的是彻底替换,不是原地整理。
为什么 OPTIMIZE TABLE 后查询反而变慢了
常见错误现象包括:EXPLAIN 显示 rows 估算翻倍、innodb_buffer_pool_reads 突增、慢查询没减少甚至新增。
-
Data_free下降了,但碎片率本来就不高(比如Data_free / (Data_length + Index_length) ),优化纯属白忙 - 统计信息重置后,优化器选错索引:原本走
idx_created_at的查询,突然改用全表扫描 - 缓冲池冷启动:刚重建完,缓存里没热数据,首条查询触发大量磁盘读
- 锁未真正释放:
SHOW PROCESSLIST里还卡着Waiting for table metadata lock,说明优化根本没执行完,只是挂起了
比 OPTIMIZE TABLE 更可控的替代方案
如果你要在线操作、避免执行计划抖动、或只想重排数据不碰统计信息,优先考虑这些:
- MySQL 8.0.23+:用
ALTER TABLE t REBUILD—— 只重排页,不更新统计信息,ALGORITHM=INPLACE且不退化,锁粒度最小 - 所有 5.6+ 版本:用
ALTER TABLE t FORCE—— 语义明确是“重建”,不会被误判成结构变更,比OPTIMIZE TABLE更可预期 - 想保留旧统计信息?跳过自动
ANALYZE:显式执行ALTER TABLE t ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE,之后再按需ANALYZE TABLE t
别把 OPTIMIZE TABLE 当定时任务跑。它解决的是已确认的碎片问题,不是保健操。真正该盯的是 Data_free 占比、innodb_buffer_pool_read_requests / innodb_buffer_pool_reads 趋势、以及 performance_schema.table_io_waits_summary_by_index_usage 里那些长期 COUNT_STAR = 0 的索引——删掉它们,比优化十次更有效。











