索引未生效是因元数据与B+树结构脱节,需用SHOW INDEX确认Cardinality非零,ALTER TABLE ... FORCE强制重建更可靠。

ALTER TABLE重建索引时为什么没生效
MySQL数据恢复后索引“看起来还在”,但查询变慢、EXPLAIN显示没走索引,大概率是索引元数据和实际B+树结构脱节了——尤其是用物理文件拷贝(如直接复制ibd文件)或mysqldump未带--skip-triggers --skip-extended-insert等选项恢复时,InnoDB的聚簇索引和二级索引可能处于不一致状态。
此时执行ALTER TABLE t ENGINE=InnoDB或ALTER TABLE t ADD INDEX idx_x(x)看似成功,但旧索引页没被真正清理,新索引也没完全接管查询路径。
- 优先用
SHOW INDEX FROM t确认索引是否真实存在且Cardinality非零;若Cardinality为NULL或0,说明统计信息失效,索引大概率不可用 - 不要只依赖
DESCRIBE t或SHOW CREATE TABLE t——它们只显示DDL定义,不反映存储引擎层的真实索引结构 - 对大表,
ALTER TABLE ... FORCE比单纯ENGINE=InnoDB更可靠,它强制重建整个表(含所有索引),而非仅触发隐式重建
MyISAM表恢复后索引损坏的特殊处理
MyISAM不支持事务,索引文件(.MYI)和数据文件(.MYD)分离,恢复时若只复制了.MYD而忽略.MYI,或者REPAIR TABLE中途失败,就会出现“索引存在但查不到数据”或“Incorrect key file for table”错误。
- 先运行
myisamchk -s /var/lib/mysql/db/t.MYI检查索引文件完整性;报错就立刻停手,别再写入 - 修复必须用
myisamchk -r -v /var/lib/mysql/db/t.MYI(-r是关键,-safe-recover在严重损坏时反而会跳过部分索引页) - 修复后务必重启MySQL或执行
FLUSH TABLES,否则缓存中的旧索引描述符仍可能被使用
重建索引后查询仍不走索引的排查点
即使ALTER TABLE返回成功,EXPLAIN仍显示type: ALL,常见原因不是索引没建好,而是优化器主动弃用了它。
- 检查
SELECT语句里是否对索引列用了函数或表达式,比如WHERE YEAR(create_time) = 2023——这会让create_time上的索引完全失效 - 确认查询条件中索引列的
COLLATION和字段定义一致,比如字段是utf8mb4_0900_as_cs,但WHERE name = 'abc'隐式转成utf8mb4_general_ci,就无法命中索引 - 运行
ANALYZE TABLE t更新统计信息,特别是恢复后表行数剧变(比如从10万变成1000万),旧统计会导致优化器误判索引效率
避免下次恢复再出问题的关键操作
数据恢复不是“把文件放回去就完事”,InnoDB和MyISAM对一致性要求完全不同,混用方法必然埋雷。
- InnoDB表优先用
mysqlpump或mysqldump --single-transaction --routines --triggers导出,恢复时确保innodb_force_recovery没被意外开启(它会跳过索引校验) - 物理恢复(如从备份服务器拷
ibd)前,必须先ALTER TABLE t DISCARD TABLESPACE,再COPY文件,最后IMPORT TABLESPACE——少一步,索引就悬空 - 恢复后第一件事不是查业务数据,而是跑
SELECT COUNT(*) FROM t和SELECT COUNT(*) FROM t USE INDEX (idx_x)对比结果,数量不一致就是索引没生效
最麻烦的情况是:索引页部分损坏,但MySQL没报错,查询偶尔走索引、偶尔全表扫。这种问题不会立刻暴露,得靠持续监控Handler_read_*状态变量才能发现。



















