真正需重构索引的判断标准是:DATA_FREE/(DATA_LENGTH+INDEX_LENGTH)>20%、EXPLAIN中rows估算偏差超3倍且key_len异常小、innodb_buffer_pool_reads持续上升;OPTIMIZE TABLE与ALTER TABLE ENGINE=InnoDB在8.0+默认等价,但后者语义更明确且可显式控制ALGORITHM与LOCK;单索引重建应使用DROP+CREATE组合以最小化锁;大表须用pt-online-schema-change或ALTER TABLE REBUILD;重构后必须执行ANALYZE TABLE更新统计信息。

定期重构索引不能靠“定时跑 OPTIMIZE TABLE”解决,InnoDB 的碎片清理本质是重建 B+ 树结构,而盲目重建会引发锁、空间不足、执行计划突变等真实故障。
怎么判断当前表真需要重构索引?
别看 SHOW TABLE STATUS 里 Data_free 是不是非零——它只是个粗略指标。真正该盯的是碎片率和实际性能信号:
-
DATA_FREE / (DATA_LENGTH + INDEX_LENGTH)> 20% 才算显著碎片(用INFORMATION_SCHEMA.TABLES查) - 慢查询中
EXPLAIN显示rows估算值比实际扫描行数大 3 倍以上,且key_len异常小,大概率是统计信息失真 + 碎片共存 - 监控到
innodb_buffer_pool_reads持续上升,但innodb_buffer_pool_read_requests变化不大,说明物理 I/O 增加,页利用率下降
ALTER TABLE ... ENGINE=InnoDB 和 OPTIMIZE TABLE 到底差在哪?
在 MySQL 8.0+ 中,OPTIMIZE TABLE t 默认等价于 ALTER TABLE t ENGINE=InnoDB, ALGORITHM=INPLACE,但行为不完全可控:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 如果表含全文索引、生成列、外键或使用了
ROW_FORMAT=COMPRESSED,MySQL 会自动退化为ALGORITHM=COPY,触发全表排他锁(DML 完全阻塞) -
ALTER TABLE t ENGINE=InnoDB语义更明确,脚本中可配合ALGORITHM=INPLACE LOCK=NONE显式约束(需满足在线 DDL 条件) - 两者都会重建所有索引(包括聚簇索引),但都不会自动更新统计信息——必须后续手动执行
ANALYZE TABLE t
只想重建单个低效索引,又不想锁整张表?
用 DROP INDEX + CREATE INDEX 组合,这是唯一能精准控制范围、支持 ALGORITHM=INPLACE 且只锁索引元数据的操作:
- 二级索引重建不涉及数据行重排,耗时短、空间开销小(仅需新索引的存储空间)
- 执行期间原索引仍可用,查询不受影响;只有
DROP那一瞬间会短暂升级元数据锁(MDL),一般毫秒级 - 注意:不要在主从架构的从库上直接操作——若
binlog_row_image=MINIMAL,可能导致复制中断
线上大表根本不敢停写,还能重构吗?
可以,但必须放弃“单命令解决”的幻想。MySQL 原生命令做不到真正的无锁重构,可靠路径只有两条:
- 用
pt-online-schema-change(Percona Toolkit):它通过影子表 + 触发器同步增量变更,最终原子切换,全程业务可写 - MySQL 8.0.23+ 可试
ALTER TABLE t REBUILD:只重排数据页和索引页,不重建结构、不更新统计信息,开销最小,但之后必须补ANALYZE TABLE
真正容易被忽略的点是:重构后不执行 ANALYZE TABLE,优化器继续用旧的统计信息生成执行计划,性能可能不升反降——这不是碎片问题,是统计失真问题。

















