MySQL 5.7 频繁被 Systemd 杀死:一次惊心动魄的故障排查之旅

摘要 :本文详细记录了一次 MySQL 5.7 数据库频繁宕机的问题排查全过程。从应用连接失败日志入手,逐步深入分析 systemd 服务配置、磁盘空间、启动超时等关键因素,最终通过优化 systemd 服务类型完美解决问题。适合所有遇到过数据库莫名重启的开发者阅读。

📌 问题背景

在一个基于 Spring Boot + Druid 连接池 + MySQL 5.7 的项目中,应用突然无法连接数据库,日志中频繁出现以下错误:

java

perl 复制代码
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago.

错误码为 errorCode 0, state 08S01,这是一个典型的网络通信故障。但奇怪的是,网络是通的,MySQL 服务也在运行,为什么会连接失败?

🔍 问题现象

1. 应用日志特征

应用日志显示了一个规律性的错误模式:

text

makefile 复制代码
13:24:40 - 13:24:44  (持续4秒,密集重试)
13:29:44 - 13:29:52  (持续8秒)
13:34:53 - 13:35:00  (持续7秒)
13:40:03 - 13:40:08  (持续5秒)

每隔 5 分钟,应用就会爆发一波连接失败的错误。 这强烈暗示 MySQL 服务在频繁重启。

2. MySQL 错误日志分析

查看 MySQL 错误日志 /opt/tar/logs/error.log,发现了关键线索:

text

bash 复制代码
2026-06-21T05:40:08.788502Z mysqld_safe Starting mysqld daemon with databases from /opt/tar/data
2026-06-21T05:40:09.064660Z mysqld_safe /opt/mysql-5.7.28/bin/mysqld: ready for connections.
2026-06-21T05:45:11.741655Z mysqld_safe mysqld from pid file /opt/tar/mysqld.pid ended

MySQL 在 13:40:08 成功启动,但在 13:45:11 就被关闭了。每次运行时间仅约 5 分钟,这就是应用连接失败的根本原因。

3. Systemd 日志揭示真相

查看 systemd 日志:

bash

css 复制代码
journalctl -u mysql -n 50 --no-pager

关键信息浮出水面:

text

less 复制代码
Jun 20 23:43:42 VM-0-2-centos systemd[1]: mysql.service start operation timed out. Terminating.
Jun 20 23:43:45 VM-0-2-centos systemd[1]: Failed to start MySQL Server.
Jun 20 23:43:50 VM-0-2-centos systemd[1]: mysql.service holdoff time over, scheduling restart.

Systemd 认为 MySQL 启动超时,强制杀死了 MySQL 进程! 虽然实际上 MySQL 已经启动成功了。

🔧 排查过程

第一步:检查磁盘空间

bash

bash 复制代码
df -h /

结果触目惊心:

text

bash 复制代码
/dev/vda1        40G   38G  295M 100% /

磁盘已满! 这解释了为什么 MySQL 启动缓慢,但这不是根本原因,只是加剧了问题。

清理磁盘空间:

bash

bash 复制代码
# 清理 systemd journal
journalctl --vacuum-size=500M

# 清理 yum 缓存
yum clean all

# 清理临时文件
rm -rf /tmp/*

# 清理旧日志
find /var/log -name "*.log" -mtime +7 -delete

第二步:分析 Systemd 服务配置

查看当前的 systemd 服务配置:

bash

bash 复制代码
cat /etc/systemd/system/mysql.service

配置内容如下:

ini

ini 复制代码
[Unit]
Description=MySQL Server
After=network.target
After=syslog.target

[Service]
TimeoutStartSec=300
TimeoutStopSec=300
Type=forking
User=mysql
Group=mysql
ExecStart=/opt/mysql-5.7.28/bin/mysqld_safe --defaults-file=/opt/tar/my.cnf --socket=/opt/tar/mysqld.sock --user=mysql
ExecStop=/opt/mysql-5.7.28/bin/mysqladmin -uroot -p'XX@XXlimp.com' -S /opt/tar/mysqld.sock shutdown
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

虽然 TimeoutStartSec=300 已经设置了 300 秒超时,但为什么 systemd 还在 90 秒就杀死进程?

第三步:问题定位 --- Type=forking 的陷阱

关键发现 :服务类型 Type=forking 导致了问题!

Type=forking 时,systemd 的行为是:

  1. 启动 mysqld_safe 父进程
  2. mysqld_safe 启动 mysqld 子进程
  3. mysqld_safe 父进程退出
  4. systemd 认为服务启动完成

但在某些情况下(如磁盘慢、配置复杂),mysqld_safe 没有及时通知 systemd 启动状态,导致 systemd 误判为超时,然后杀死了整个进程树。

✅ 解决方案

最终配置:Type=simple

将服务类型从 forking 改为 simple,并直接启动 mysqld 而不是 mysqld_safe

ini

ini 复制代码
[Unit]
Description=MySQL Server
After=network.target
After=syslog.target

[Service]
Type=simple
User=mysql
Group=mysql
ExecStart=/opt/mysql-5.7.28/bin/mysqld --defaults-file=/opt/tar/my.cnf --socket=/opt/tar/mysqld.sock --user=mysql
ExecStop=/opt/mysql-5.7.28/bin/mysqladmin -uroot -p'XXXXX.com' -S /opt/tar/mysqld.sock shutdown
TimeoutStartSec=300
TimeoutStopSec=300
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

关键变化

  • Type=forkingType=simple
  • ExecStartmysqld_safe 改为直接启动 mysqld

为什么这样修改有效?

配置项 Type=forking Type=simple
启动方式 通过包装脚本启动 直接启动主进程
systemd 监控 监控父进程退出 监控主进程状态
超时判断 依赖父进程通知 直接判断进程状态
稳定性 易受环境因素影响 精确可靠

Type=simple 让 systemd 直接监控 mysqld 主进程,不再依赖 mysqld_safe 的 fork 行为,从而避免了超时误判。

📊 验证修复

1. 重新加载配置并启动

bash

bash 复制代码
# 停止服务
systemctl stop mysql

# 重新加载 systemd
systemctl daemon-reload

# 启动服务
systemctl start mysql

# 查看状态
systemctl status mysql

2. 监控运行状态

bash

bash 复制代码
# 查看运行时间
mysql -uroot -p -S /opt/tar/mysqld.sock -e "SHOW STATUS LIKE 'Uptime';"

# 持续监控 10 分钟
watch -n 60 'systemctl status mysql | grep -E "Active|since"'

3. 验证应用连接

bash

bash 复制代码
# 测试应用连接
mysql -uroot -p -h127.0.0.1 -P3306 -e "SELECT 1"

修复后,MySQL 持续稳定运行,应用连接恢复正常,错误日志不再出现连接失败。

💡 经验总结

1. Systemd 服务类型的选择

  • Type=simple:适用于大多数场景,直接运行主进程,推荐使用
  • Type=forking:仅当服务主动 fork 子进程且父进程退出时使用,容易产生超时误判
  • Type=notify:服务主动通知 systemd 启动完成,最精确但需要服务支持

2. 故障排查方法论

  1. 从应用日志入手:观察错误模式和规律
  2. 检查服务状态systemctl statusjournalctl 是关键工具
  3. 查看服务日志 :MySQL 的 error.log 是定位问题的金矿
  4. 检查系统资源:磁盘、内存、CPU 都可能影响服务稳定性
  5. 审查服务配置:有时候问题就藏在配置文件的细节里

3. 预防措施

  • 监控磁盘空间,设置告警阈值(建议 < 80%)
  • 配置日志轮转,防止日志无限增长
  • 定期审查 systemd 服务配置
  • 建立服务健康检查机制

🎯 结语

一次看似复杂的数据库连接问题,最终根源竟然是 systemd 配置不当。这提醒我们:在排查问题时,不要只盯着应用层,更要关注基础设施的每一个细节。

Systemd 作为 Linux 系统的 init 系统,其配置直接影响服务的稳定性。理解 Type 参数的含义,选择合适的服务类型,是保障服务稳定运行的基础。

希望本文能帮助遇到类似问题的开发者少走弯路。


相关命令速查

bash

bash 复制代码
# 查看服务状态
systemctl status mysql

# 查看服务日志
journalctl -u mysql -n 50

# 查看服务配置
systemctl cat mysql

# 修改服务配置
systemctl edit mysql.service

# 重新加载配置
systemctl daemon-reload

# 监控磁盘空间
df -h

# 清理 journal 日志
journalctl --vacuum-size=500M
相关推荐
行思理1 小时前
fastadmin 新手部分功能点
后端·php
Fanta丶1 小时前
5.Activiti7工作流启动和查询流程、流程任务处理
后端
꒰ঌ 安卓开发໒꒱1 小时前
.NET CAP入门到入土
后端·c#·.net
sukioe2 小时前
【无标题】
后端·docker·centos
geovindu3 小时前
java:Abstract Factory Pattern
java·开发语言·后端·设计模式·抽象工厂模式·创建型模式
geovindu3 小时前
java: Factory Method Pattern
java·开发语言·后端·设计模式·工厂方法模式·创建型模式
voicelen3 小时前
一通电话背后有多少角色?我拆了 Voicelen 的调度台给你看
后端
用户8017220416393 小时前
BarQode:C# 条码二维码生成库
后端