迁移后索引“存在但不生效”的主因是Cardinality为0或极低,表明统计信息未更新,导致优化器弃用索引;应优先执行ANALYZE TABLE而非盲目重建索引。

迁移后索引“存在但不生效”,90%以上的情况不是索引丢了,而是Cardinality为 0 或极低,优化器直接放弃使用——根源几乎总是统计信息未更新,而非索引本身损坏。
为什么 SHOW INDEX 显示索引存在,EXPLAIN 却走 ALL?
这是最典型的误判起点。执行 SHOW INDEX FROM table_name 看到索引名、列名都齐全,就以为万事大吉,但关键字段 Cardinality(基数)可能全为 0。这意味着 MySQL 认为该索引“对查询选择性毫无帮助”,于是跳过它,退化为全表扫描。
-
Cardinality = 0是硬信号:统计信息完全缺失或严重过期,哪怕索引物理存在也形同虚设 - 常见于
mysqldump导入后、DRS 全量迁移后、或先建表再批量导入数据的场景 - 注意区分:如果
SHOW CREATE TABLE里连KEY定义都消失了,才是索引真丢失;而Cardinality异常是“有索引,不用索引”
ANALYZE TABLE 是最快见效的修复动作
这不是可选项,是迁移后必须立即执行的步骤。MySQL 不会在导入数据后自动触发统计信息收集,尤其当数据量大、导入方式绕过常规 INSERT 流程时(如 LOAD DATA INFILE、DRS 并行写入)。
- 对单表执行:
ANALYZE TABLE stock_in_detail; - 对库内所有表批量执行(谨慎用于生产):
SELECT CONCAT('ANALYZE TABLE ', table_schema, '.', table_name, ';') FROM information_schema.tables WHERE table_schema = 'your_db';,复制结果后执行 - 效果立竿见影:再次
EXPLAIN,key字段会立刻显示命中索引,rows_examined大幅下降 - 不锁表:
ANALYZE TABLE在 InnoDB 上是轻量操作,仅需元数据锁(MDL),不影响 DML
重建索引前先确认是否真需要 ALGORITHM=INPLACE
盲目执行 ALTER TABLE t ENGINE=InnoDB 或 REPAIR TABLE 不但慢,还可能引发长时间锁表。绝大多数迁移后的“索引失效”问题,ANALYZE TABLE 就已解决;只有在确认索引定义异常(如前缀长度被截断、collation 不一致导致隐式转换)时,才需重建。
- 检查索引定义是否完整:
SHOW CREATE TABLE table_name,重点看UNIQUE KEY、FULLTEXT、以及 VARCHAR 列的括号长度(如title(191))是否还在 - 跨字符集迁移(如 utf8 → utf8mb4)后,原
VARCHAR(255)索引可能因超 767 字节限制被静默降级,此时需显式重建:CREATE INDEX idx_title ON posts(title(191)) ALGORITHM=INPLACE; - 主键/唯一索引重建必须加
ALGORITHM=INPLACE, LOCK=NONE,否则 5.6+ 版本默认仍可能升级为LOCK=SHARED
容易被忽略的配置漂移和缓存重置
迁移后性能差,有时跟索引无关,而是环境参数或缓存状态突变。这些不会报错,但会让所有查询变钝。
-
innodb_buffer_pool_size若未按目标机器内存重新设置,缓存命中率归零,首次查询全部走磁盘 -
sort_buffer_size和join_buffer_size过小,会导致原本走索引的 ORDER BY / JOIN 退化为磁盘临时表 - 连接池配置(如 max_connections、wait_timeout)若与应用端不匹配,会频繁触发重连或
Lost connection,表现为偶发超时 - 时区不一致(
system_time_zonevstime_zone)会让基于时间的查询条件无法命中索引,例如WHERE create_time > '2026-04-01'实际比较的是错误时区值
真正麻烦的从来不是重建索引本身,而是把 Cardinality 归零当成“索引坏了”去折腾 DDL,结果耽误半天才发现只缺一条 ANALYZE TABLE。统计信息不是可选附件,它是优化器做决策的唯一依据——迁移后不刷它,等于让数据库“睁眼瞎”。


















