会,alter table ... engine=innodb 通过重建表间接重建所有索引,包括聚簇索引和二级索引;optimize table 在 innodb 中等价于该操作,可整理碎片、重排b+树,但属全表级重建。

ALTER TABLE ... ENGINE=InnoDB 会重建所有索引吗?
会,但不是“重建索引”本身,而是通过重建表来间接重建所有索引。MySQL 在执行 ALTER TABLE t ENGINE=InnoDB(或 ALGORITHM=INPLACE 不支持时的默认行为)时,会创建新表、逐行拷贝数据、重新生成聚簇索引和所有二级索引。这相当于一次全量索引重建,但开销远大于单纯优化索引结构。
适用场景:表存在严重碎片、页分裂率高、或需要变更行格式(如从 COMPACT 切到 DYNAMIC)。不推荐仅因个别索引性能下降就用此法。
- 在线 DDL 支持程度取决于 MySQL 版本和操作类型;5.6+ 对多数二级索引操作支持
ALGORITHM=INPLACE,无需锁表 - 执行前务必确认磁盘空间充足——临时表可能占用等量空间
- 若只改索引,优先用
ALTER TABLE t DROP INDEX idx_name, ADD INDEX idx_name (col),比换引擎轻量得多
OPTIMIZE TABLE 能真正“整理”索引碎片吗?
在 InnoDB 中,OPTIMIZE TABLE 实际等价于 ALTER TABLE ... ENGINE=InnoDB(并带 ANALYZE TABLE),它确实能回收空闲页、合并相邻页、重排 B+ 树节点,从而降低索引层级、提升范围扫描效率。但它不是“局部修复”,而是全表级重建。
注意:OPTIMIZE TABLE 在 MySQL 8.0+ 默认启用 ALGORITHM=INPLACE 的前提下,对某些操作仍会退化为 COPY 模式(例如表含全文索引或使用了加密表空间)。
- 不要在业务高峰期运行;即使支持 INPLACE,也会持有
S锁较长时间 - 对小表(
- 监控索引碎片更可靠的方式是查
information_schema.INNODB_SYS_INDEXES的PAGE_COUNT和DISTINCT_KEY_COUNT比值,或用SHOW INDEX FROM t观察Cardinality是否长期偏低
如何安全地重建单个二级索引而不锁表?
MySQL 5.7+ 支持 ALGORITHM=INPLACE, LOCK=NONE 重建单个索引,前提是该索引不涉及主键、不被外键引用、且表引擎为 InnoDB。
典型命令:ALTER TABLE t DROP INDEX idx_a, ADD INDEX idx_a (col1, col2) ALGORITHM=INPLACE, LOCK=NONE;
- 必须同时写
DROP和ADD—— 分两步执行会短暂丢失索引,导致查询走全表扫描 - 如果提示
ALGORITHM=INPLACE is not supported,说明当前操作触发了 COPY 模式(比如修改了列顺序或加了INCLUDE字段),此时应评估是否真需重建,或改用 pt-online-schema-change - 重建过程中,
INFORMATION_SCHEMA.INNODB_TRX可能显示长事务阻塞 DDL;建议提前 kill 掉非必要长事务
重建索引后为什么查询没变快?
常见原因不是索引没重建成功,而是统计信息未更新或执行计划未刷新。InnoDB 的索引基数(Cardinality)由采样估算得出,重建后不一定自动重采样;而优化器依赖这些统计值选择是否走索引。
- 强制更新统计信息:
ANALYZE TABLE t;(轻量,推荐每次重建后执行) - 检查实际执行计划:
EXPLAIN FORMAT=JSON SELECT ...,确认used_key_parts和rows_examined_per_scan是否符合预期 - 留意隐式类型转换:哪怕索引重建了,
WHERE status = '1'(status 是 INT)仍会导致索引失效 —— 这类问题不会因重建索引而修复 - 如果用了 Query Cache(已弃用)或客户端缓存了预编译语句,也可能看到“旧计划”,需清空或重启连接
ANALYZE TABLE 的时机和 EXPLAIN 验证动作——没有这两步,重建很可能白忙一场。










