mysql如何重建索引_mysql索引维护实践

6次阅读

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

mysql如何重建索引_mysql索引维护实践

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_INDEXESPAGE_COUNTDISTINCT_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;

  • 必须同时写 DROPADD —— 分两步执行会短暂丢失索引,导致查询走全表扫描
  • 如果提示 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_partsrows_examined_per_scan 是否符合预期
  • 留意隐式类型转换:哪怕索引重建了,WHERE status = '1'(status 是 INT)仍会导致索引失效 —— 这类问题不会因重建索引而修复
  • 如果用了 Query Cache(已弃用)或客户端缓存了预编译语句,也可能看到“旧计划”,需清空或重启连接

重建索引这件事,表面是操作,底层是数据分布、采样逻辑、优化器决策三者的耦合。最容易被忽略的,其实是 ANALYZE TABLE 的时机和 EXPLAIN 验证动作——没有这两步,重建很可能白忙一场。

text=ZqhQzanResources