---
title: 开发日记/记一次mysql数据库恢复引发的慢查询问题
published: 2026-01-10T00:11:06
description: ''
image: ''

draft: false 
lang: ''
category: '开发日记'
---

# 记一次 MySQL 数据库恢复引发的慢查询问题

## 案发经过
今天在公司遇到个诡异的问题。另一位实习生同事对测试环境的 MySQL 数据库进行了恢复操作。原本以为只是常规操作，结果恢复完成后，昨天还能在 2 秒内跑完的查询，今天跑了 10 分钟都没出结果。

此时测试服务器的情况非常糟糕：CPU 占用率直接飙升到 100%，内存占用也居高不下。

##  排查过程

### 1. 初步检查
找技术经理用 Root 账号登录数据库查看状态，发现那几个查询语句一直在执行中，且耗时都已经超过了 10 分钟。
尝试 Kill 掉这些查询进程后，服务器的 CPU 和内存瞬间恢复正常。但只要业务端再次触发那个 SQL 查询，服务器立马又“瘫痪”了。

### 2. 尝试重启
起初怀疑是 SQL 导致了死锁，或者有什么隐藏进程阻塞了数据库。于是简单粗暴地重启了几次数据库服务，但问题依旧，没有任何改善。

### 3. 分析执行计划
既然重启无效，只能深入分析了。
查看慢查询日志，确认就是那几个业务查询在拖后腿。
接着查看这些 SQL 的执行计划（`EXPLAIN`），惊讶地发现：**很多查询竟然都不走索引了！** 全表扫描导致了查询时间无限拉长。

这就很离谱了。这些 SQL 在昨天（恢复数据前）还能正常运行，且正式环境也是完全相同的 SQL 和索引结构，一直运行良好。这说明问题不在 SQL 写法或索引缺失上。

## 解决

技术经理建议尝试优化 SQL 或添加索引，但我认为既然正式环境没问题，且昨天也没问题，说明结构本身是合理的。
结合“刚做过数据库恢复”这个操作，我推测可能是**数据库的统计信息（Statistics）在恢复过程中丢失或失效了**，导致 MySQL 优化器误判，选择了全表扫描而不是走索引。

于是，我尝试对相关表执行了分析命令：

```sql
ANALYZE TABLE 表名;
```

**结果立竿见影！**
执行完分析后，再次查看执行计划，查询终于重新走了索引。业务查询速度也恢复到了秒级，服务器负载瞬间降了下来。

## 💡 深度解析：关于 ANALYZE TABLE

### 1. 它到底干了什么？
`ANALYZE TABLE` 的主要作用是**重新统计索引的基数（Cardinality）**。
MySQL 的优化器（Optimizer）在决定是否使用索引时，依据的是成本模型（Cost Model）。而成本计算的核心输入就是**索引区分度**。
*   MySQL 会随机采样数据页（InnoDB 默认采样 8 个页，可配置）。
*   估算每个索引中有多少个不同的值（基数）。
*   如果采样结果显示某个索引的基数太小（重复值太多），优化器就会认为“反正大部分数据都要读，不如直接全表扫描快”，从而放弃索引。

在数据恢复或大量导入后，数据分布发生了剧烈变化，但统计信息可能还是旧的（或者为空），导致优化器拿着错误的情报做出了错误的决策。

### 2. 各个 MySQL 版本的表现
*   **MySQL 5.5 及以前**：统计信息是**非持久化**的。重启数据库后，统计信息会丢失，MySQL 会在第一次访问表时重新计算。这通常不会导致一直不走索引，但可能会导致数据库刚启动时产生瞬间的性能抖动。
*   **MySQL 5.6 及以后（主流）**：引入了**持久化统计信息（Persistent Optimizer Statistics）**，默认开启（`innodb_stats_persistent=ON`）。统计信息存储在磁盘的 `mysql.innodb_table_stats` 表中。
    *   **优点**：重启后统计信息不丢失，执行计划稳定。
    *   **缺点**：这也意味着，如果你通过非标准方式（如直接拷贝文件、某些恢复工具）恢复数据，或者大量数据变动后自动更新机制（`innodb_stats_auto_recalc`）没有及时触发，统计信息就会长期处于“过时”状态，必须手动 `ANALYZE` 才能纠正。

### 3. 这种问题出现的概率大吗？
**非常大，尤其是逻辑备份恢复后。**
*   **逻辑恢复（如 source .sql 文件）**：本质是执行成千上万条 `INSERT` 语句。虽然 InnoDB 默认在表数据变更超过 10% 时会自动重新计算统计信息，但在高负载恢复过程中，这个异步动作可能会滞后，或者因为锁竞争而失败。
*   **物理恢复（如 XtraBackup）**：通常会连同统计信息表文件一起恢复，出现概率相对较小，但也存在统计信息损坏的可能。

**最佳实践**：在生产环境中，每当进行**全量数据迁移、大批量数据导入/删除**操作后，建议把 `ANALYZE TABLE` 作为标准收尾动作执行一遍，以确保“情报”准确。

## 📝 总结
数据库恢复或大量数据导入导出后，可能会导致索引统计信息不准确。MySQL 优化器依赖这些统计信息来决定执行计划。当统计信息偏差过大时，优化器可能会放弃索引而选择全表扫描。

下次遇到这种“昨天好好的，今天突然不走索引”的情况，且 SQL 没变动，不妨先 `ANALYZE TABLE` 一下，强制更新统计信息。


---
