恢复后查询慢大概率因优化器使用过时统计信息而非数据问题;必须执行ANALYZE TABLE更新索引基数、行数等统计信息,否则优化器误判选择性导致全表扫描或索引失效。

恢复后的表查询慢,大概率是优化器用了过时的统计信息,而不是数据本身有问题
为什么 ANALYZE TABLE 是必须的
MySQL 的优化器依赖 INFORMATION_SCHEMA.STATISTICS 和内存中的统计信息(如索引基数、行数估算)来决定是否走索引、选哪个索引、用什么连接顺序。备份恢复(尤其是物理恢复、mysqldump 导入、ALTER TABLE ... IMPORT TABLESPACE)不会自动触发统计信息更新,导致优化器仍按旧表结构或旧数据分布做决策——比如误判某列选择性差而放弃索引,实际却该走。
常见现象包括:
-
EXPLAIN显示type为ALL或index,key为NULL,哪怕字段明明有索引 - 相同 SQL 在原库秒出,在恢复库跑几十秒甚至超时
-
SHOW TABLE STATUS中的Rows值明显不准(比如显示 1 行但实际百万)
ANALYZE TABLE 怎么执行才有效
它不是“执行一次就永久生效”,而是针对当前表立即采样并更新内存+持久化统计信息。注意以下实操要点:
- 对单表执行即可:
ANALYZE TABLE mydb.users;,不需要加锁(InnoDB 下是轻量级 DML,不影响读写) - 避免在业务高峰期全库执行:
SELECT CONCAT('ANALYZE TABLE ', table_schema, '.', table_name, ';') FROM information_schema.tables WHERE table_schema = 'mydb';生成语句后分批跑 - 如果表极大(>10GB),可控制采样精度:
SET innodb_stats_persistent_sample_pages = 128;(默认 20),再执行ANALYZE,防止采样耗时过长 - 确认生效:查
INFORMATION_SCHEMA.STATISTICS或对比SHOW INDEX FROM mydb.users中的Cardinality是否变化
哪些恢复方式特别容易漏掉 ANALYZE
不是所有恢复流程都会自动触发统计信息刷新,这几类要手动补:
-
mysqldump --single-transaction+mysql < dump.sql:导入后Rows常为 0 或极小值,必须ANALYZE - 使用
xtrabackup物理恢复后未运行innobackupex --apply-log后的ANALYZE(虽然 apply-log 会部分更新,但不保证完整) - 从 RDS 备份恢复到自建库、或跨版本恢复(如 5.7 → 8.0):统计信息格式兼容性可能出问题
- 用
CREATE TABLE AS SELECT或INSERT INTO ... SELECT拷贝大表后:新表无统计信息
比 ANALYZE 更隐蔽的问题:innodb_stats_persistent
MySQL 5.6+ 默认开启 innodb_stats_persistent = ON,意味着统计信息会落盘到 mysql.innodb_table_stats。但恢复时若没同步这张系统表(比如只恢复业务库),就会沿用旧的持久化统计——此时 ANALYZE TABLE 不仅更新内存,也会重写这张表;但如果权限不足或表损坏,可能静默失败。检查方法:
- 运行
SELECT * FROM mysql.innodb_table_stats WHERE database_name = 'mydb' AND table_name = 'users'; - 若结果为空或
last_update是恢复前时间戳,说明持久化统计没更新到位 - 必要时可先
DROP TABLE mysql.innodb_table_stats;(需重启或等下次ANALYZE自动重建)
最稳妥的做法:恢复完成后,对每个业务表逐个执行 ANALYZE TABLE,并用 EXPLAIN 验证关键查询是否回归预期执行计划——别信“恢复完就万事大吉”这种直觉。


















