系统化梳理 MySQL 常见故障类型、排查顺序、证据保留办法与长期治理清单,帮助运维与开发团队减少停机时间,提升数据库稳定性。

软件定制开发团队
"真正有价值的技术内容,应该能帮助客户更快判断方向、预算和落地路径。"
处理 MySQL 故障的第一步不是翻日志,而是确认问题属于哪个层面。根据过去十年参与的企业项目经验,绝大多数故障可以归入以下五类:
不同层面的排查入口差异很大。连接层先看 error log 和 netstat;查询层先看 slow log 和 processlist;存储层先看磁盘使用和系统表。遇到故障时,先问一句:这是连接不上,还是连上了跑得慢,还是数据读不出来?这个判断决定了后续 80% 的排查效率。
当故障正在发生时,时间窗口非常有限。我的建议是遵循“从外到内、先止血再根治”的顺序。
无论原因是什么,如果业务已经中断,优先恢复服务。常见做法:
这个阶段不要花时间分析根因。记录现场、保留证据、快速恢复,是唯一正确的做法。
服务恢复后,立即收集以下信息。如果等到第二天再查,很多线索已经消失。
这些证据一旦丢失,后续分析就只能靠猜。建议将收集脚本固化到运维工具中,故障时一键执行。
拿到证据后,按以下顺序排查:
1. 检查 error log 中是否有 panic、assertion failure 或 InnoDB 损坏信息。
2. 检查 slow log,找到故障时间段的 TOP N 慢查询。
3. 检查 processlist,看是否有长时间未提交的事务或锁等待。
4. 检查系统指标,看是否在故障前有内存、IO 或连接数的异常增长。
5. 检查主从状态,看延迟是否突然增大或复制线程是否停止。
一个常见误区是直接翻慢查询日志,但实际根因可能是磁盘 IO 被打满而非 SQL 本身有问题。所以排查顺序必须从系统层到数据库层再到 SQL 层。
连接故障是最常见的 MySQL 故障之一,表现形式是应用报“Can't connect to MySQL server”或“Too many connections”。
先确认 mysqld 是否存活:ps aux | grep mysqld。如果进程不存在,检查 dmesg 看是否被 OOM killer 杀掉。如果进程存在但无法连接,检查端口监听状态:netstat -tlnp | grep 3306。如果端口正常,检查 max_connections 和当前连接数:
```
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
```
如果 Threads_connected 接近 max_connections,说明连接数打满。此时可以临时增加 max_connections,但根本办法是排查连接泄漏。如果连接数正常但应用仍然报错,检查应用服务器到数据库的网络连通性,telnet 数据库IP 3306 看是否通。不通则检查防火墙和安全组。
慢查询的排查经常陷入“上来就加索引”的误区。实际上,慢查询的根因可能是索引失效、表数据量过大、服务器资源不足、锁竞争或者 SQL 写法问题。
1. 确认慢查询日志已开启,并设置 long_query_time 为 1 秒或更低。
2. 在故障时间段内,从 slow log 中提取 TOP 5 慢查询。
3. 对每条慢查询执行 EXPLAIN,检查 type、rows、Extra 字段。
4. 检查表的行数和数据分布:SHOW TABLE STATUS 看行数,SHOW INDEX 看索引基数。
5. 检查服务器资源:CPU 是否打满、磁盘 IO 是否高、内存是否不足导致排序使用磁盘临时表。
遇到这些情况,需要从业务逻辑或 SQL 写法角度优化,而不是堆索引。
死锁在 InnoDB 引擎下是正常现象,事务并发越高,死锁概率越大。MySQL 会自动检测死锁并回滚其中一个事务,应用层只需要捕获死锁异常并重试。
使用 SHOW ENGINE INNODB STATUS 查看 LATEST DETECTED DEADLOCK 段。该段会显示两个事务各自持有的锁和等待的锁,以及回滚的事务。
典型的死锁场景是:事务 A 更新行 1,事务 B 更新行 2,然后事务 A 等待行 2,事务 B 等待行 1。排查时关注两条 SQL 的访问顺序是否一致。
主从延迟是 MySQL 复制架构中最常见的故障之一。延迟分为正常延迟和异常延迟,处理方式完全不同。
主库写入量超过从库单线程(或单 worker)的回放能力。在 MySQL 5.7 及以下版本中,从库默认单线程回放 relay log,如果主库写入量大,延迟是必然的。解决方案:
从库 SQL 线程因错误或锁等待停止。排查步骤:
1. SHOW SLAVE STATUS\G,检查 Slave_IO_Running 和 Slave_SQL_Running 是否为 Yes。
2. 如果 SQL 线程为 No,查看 Last_Error 字段。
3. 常见错误:主键冲突、表不存在、表结构不一致、死锁。
4. 根据错误类型处理:跳过错误(SET GLOBAL sql_slave_skip_counter = 1)或手动修复数据。
主从延迟会导致从库读到旧数据,但不会导致数据不一致。数据不一致通常由以下原因引起:
定期使用 pt-table-checksum 和 pt-table-sync 检查并修复主从数据一致性,建议每周一次。
磁盘写满导致的故障通常表现为:应用报错“The table 'xxx' is full”、无法写入数据、mysqld 启动失败。
1. df -h 查看磁盘使用率。
2. du -sh /var/lib/mysql 查看数据库目录大小。
3. 进入目录后使用 du -sh * 找出最大的子目录或文件。
4. 如果是 binlog 占用过多,检查 binlog 过期时间:SHOW VARIABLES LIKE 'expire_logs_days'。如果设置为 0 或过大,手动清理:PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY。
5. 如果是 ibdata1 文件过大,需要确认是否开启了 innodb_file_per_table。如果没有开启,所有表的数据和索引都存储在共享表空间中,无法收缩。唯一的办法是重建实例。
6. 如果是 slow log 或 error log 过大,检查日志轮转配置。
实例崩溃是最严重的故障之一,通常由内存不足或内核 bug 引起。
1. 检查 dmesg 是否有 oom-killer 记录。
2. 检查 MySQL error log 是否有 crash 信息。
3. 检查系统日志 /var/log/messages 或 journalctl。
4. 如果崩溃前有大量慢查询或临时表操作,可能是临时表使用了磁盘但内存分配不合理。
很多团队在故障恢复后才发现,该留的证据没留。建议建立标准化的故障采集脚本,故障时一键执行。脚本应包含以下内容:
```
```
将脚本保存为 troubleshoot.sh,故障时执行并将输出重定向到文件。这样即使事后需要分析,也有完整的上下文。
在 SystemDo 参与的一个金融项目中,我们正是靠这套标准化流程,在数据库崩溃后 10 分钟内完成了证据采集,避免了因证据丢失导致的三天排查拉锯战。
故障排查的最终目标是减少故障发生频率。以下是一份经过多个项目验证的长期治理清单:
MySQL 故障排查不是一门玄学,而是一套可以标准化、流程化的工程实践。核心原则是:先恢复服务,再收集证据,最后分析根因。连接故障、慢查询、死锁、主从延迟、磁盘写满、实例崩溃这六类故障,覆盖了企业 MySQL 运维中 90% 以上的场景。每一类都有明确的排查入口和处理手段。
长期来看,建立监控告警、定期巡检、备份演练和变更管理机制,比掌握多少调优技巧更重要。因为数据库稳定运行的关键不是不出问题,而是出了问题能快速定位、快速恢复,并且同类问题不再重复发生。
继续了解企业数字化、SEO / GEO、AI 自动化和软件定制开发中的常见问题。