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验证动作——没有这两步,重建很可能白忙一场。
