optimize table 不适合大表分阶段,因其在不满足严格条件时退化为copy模式并全程持x锁阻塞dml,且不支持中断、进度追踪或按范围切分;真正可行的是用pt-online-schema-change分批重建或针对高碎片分区执行rebuild/reorganize,并补analyze table更新统计信息。

直接执行 OPTIMIZE TABLE 对大表(比如千万级以上)风险高、阻塞强,不能算“分阶段”。真正可落地的分阶段清理,核心是绕过全表锁、控制资源消耗、保留业务可用性——本质是用“逻辑重建 + 分批落库”替代“物理重建”。
为什么 OPTIMIZE TABLE 不适合大表分阶段?
OPTIMIZE TABLE 在 MySQL 8.0+ 默认走 ALGORITHM=INPLACE,但仅当表满足严格条件(无全文索引、无虚拟列、无外键等)才真正免锁;一旦退化为 COPY 模式,就会全程持有 X 锁,DML 完全阻塞。它本身不支持中断、进度追踪或按范围切分,谈不上“分阶段”。
- 执行中无法暂停或回滚,失败即中断,残留临时文件需手动清理
- 即使 INPLACE 成功,也会重写所有数据页和二级索引页,I/O 压力集中爆发
- 统计信息强制更新,可能触发执行计划突变,导致慢查询突然出现
用 pt-online-schema-change 实现可控分阶段重建
Percona Toolkit 的 pt-online-schema-change 是目前最成熟的大表碎片分阶段处理方案。它通过触发器捕获增量变更,在后台分批次拷贝数据,全程不锁原表(只在切换瞬间加短暂元数据锁)。
- 必须确保原表有主键或唯一非空索引,否则无法安全同步增量
- 使用
--chunk-size控制每次迁移行数(如--chunk-size=10000),避免单次事务过大 - 配合
--max-load(如"Threads_running=25")自动暂停,防止拖垮数据库负载 - 命令示例:
pt-online-schema-change \ --user=root \ --host=localhost \ --alter "ENGINE=InnoDB" \ --execute \ D=db_name,t=orders
分区表场景下优先整理高碎片分区
如果表已按时间/ID 分区(如 PARTITION BY RANGE (created_at)),碎片往往集中在近期高频写入或刚批量删除的分区。此时无需整表重建,只需针对特定分区操作:
- 查出碎片大的分区:
SELECT PARTITION_NAME, DATA_FREE FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA='db' AND TABLE_NAME='orders' AND DATA_FREE > 100*1024*1024; - 对单个分区执行优化:
ALTER TABLE orders REORGANIZE PARTITION p202608 INTO (PARTITION p202608 VALUES LESS THAN (1725177600));(MySQL 8.0.23+ 支持) - 或更轻量:
ALTER TABLE orders REBUILD PARTITION p202608;—— 只重排该分区数据页,不更新统计信息
碎片清理后必须补 ANALYZE TABLE
REBUILD 和 pt-osc 都不会自动更新统计信息,而 OPTIMIZE TABLE 虽然会更新,但可能不准(尤其大表采样率不足)。碎片清理后若跳过这步,优化器仍按旧统计估算行数,极易选错索引或执行计划。
- 对大表慎用默认采样:
ANALYZE TABLE orders;可能耗时且不准 - 推荐显式指定采样率:
ANALYZE TABLE orders UPDATE HISTOGRAM ON order_status, created_at WITH 16 BUCKETS; - 或用
innodb_stats_persistent=ON+ 定期手动触发,避免依赖自动采样
分阶段的核心不是“动作拆成几步”,而是把不可控的原子操作,换成可观测、可中断、可限流的持续过程。最容易被忽略的是:碎片清理只是释放空间和重排数据,不代表查询就变快——统计信息滞后、缓冲池预热不足、执行计划未刷新,这三个点不处理,性能提升会打折扣。










