diff --git a/src/content/posts/开发日记/记一次mysql数据库恢复引发的慢查询问题.md b/src/content/posts/开发日记/记一次mysql数据库恢复引发的慢查询问题.md new file mode 100644 index 0000000..68c23ec --- /dev/null +++ b/src/content/posts/开发日记/记一次mysql数据库恢复引发的慢查询问题.md @@ -0,0 +1,79 @@ +--- +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` 一下,强制更新统计信息。 + + +---