MySQL 通过 systemd 启动时 hang 住了……
mysqld:哥,我起不来了……
作者:贲绍华,爱可生研发中心工程师,负责项目的需求与维护工作。其他身份:柯基铲屎官。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 2100 字,预计阅读需要 7 分钟。
引言
正如题目所述,在自动化测试场景下,通过 systemd 无法启动 MySQL。
连续 kill -9
结束实例进程,检测 mysqld 在退出后是否会被正确拉起。
具体信息如下:
- 主机信息:CentOS 8(Docker 容器)
- 使用 systemd 的方式管理 mysqld 进程
- systemd service 的运行模式为:forking
- 启动命令如下:
# systemd 启动命令 sudo -S systemctl start mysqld_11690.service # systemd service 内的 ExecStart 启动命令 /opt/mysql/base/8.0.34/bin/mysqld --defaults-file=/opt/mysql/etc/11690/my.cnf --daemonize --pid-file=/opt/mysql/data/11690/mysqld.pid --user=actiontech-mysql --socket=/opt/mysql/data/11690/mysqld.sock --port=11690
现象描述
启动命令持续 hang 住,既不成功,也无任何返回,尝试几次后均无法手动复现该场景。
下图为复现场景,service 端口号不一致请忽略。
MySQL 错误日志无任何信息。查看 systemd service 状态,发现启动脚本中由于缺少参数 MAIN PID
,执行失败。
systemd 最后输出的信息为:New main PID 31036 does not exist or is a zombie
原因总结
systemd 启动 mysqld 的过程中,会先根据 service 模板中的配置,执行:
- ExecStart(启动 mysqld)
- mysqld 启动创建
pid
文件 - ExecStartPost(自定义的一些后置脚本:调整权限、将
pid
写入 cgroup 等)
在 步骤 2-3 的中间态,也就是 pid
文件刚创建出来时,主机上接收到了自动化测试下发的命令:sudo -S kill -9 $(cat /opt/mysql/data/11690/mysqld.pid)
。
由于这个 pid
文件和 pid
进程确实存在(如果不存在 kill
命令或 cat
会报错),自动化的 CASE 认为 kill
操作已成功结束。但由于 mysqld.pid
这个文件是由 MySQL 自身维护的,在 systemd 的视角中,还需要继续等待 步骤 3 完成,才认为启动成功。
在 systemd 使用 forking 模式时,会根据子进程的 PID
值判断服务是否成功启动。
如果子进程成功启动,并且没有发生意外退出,则 systemd 会认为服务已启动,并将子进程的 PID
作为 MAIN PID
。
而如果子进程启动失败或意外退出,则 systemd 会认为服务未能成功启动。
结论
在执行 ExecStartPost 时,由于子进程 ID 31036 已经被 kill
掉,后置 shell
缺少了启动参数,但 ExecStart 步骤已完成,导致 MAIN PID 31036 成为了只存在于 systemd 里的 僵尸进程。
排查过程
当遇到这个问题时是有点懵的,简单检查了一下内存、磁盘基本信息。符合预期并没有出现资源不足的情况。
先从 MySQL 的 Error Log 看看有什么发现。查看结果如下:
...无关内容省略... 2024-02-05T05:08:42.538326+08:00 0 [Warning] [MY-010539] [Repl] Recovery from source pos 3943309 and file mysql-bin.000001 for channel ''. Previous relay log pos and relay log file had been set to 4, /opt/mysql/log/relaylog/11690/mysql-relay.000004 respectively. 2024-02-05T05:08:42.548513+08:00 0 [System] [MY-010931] [Server] /opt/mysql/base/8.0.34/bin/mysqld: ready for connections. Version: '8.0.34' socket: '/opt/mysql/data/11690/mysqld.sock' port: 11690 MySQL Community Server - GPL. 2024-02-05T05:08:42.548633+08:00 0 [System] [MY-013292] [Server] Admin interface ready for connections, address: '127.0.0.1' port: 6114 2024-02-05T05:08:42.548620+08:00 5 [Note] [MY-010051] [Server] Event Scheduler: scheduler thread started with id 5
通过观察 Error Log 发现并无任何有用信息,因为启动的时间点之后无任何日志信息输出。
查看 systemctl status 确认服务当前状态:
下图为正常情况下的 status 信息:
通过对比后,整理两条有用信息:
- 后置
shell
由于缺少-p
参数导致执行失败(-p
参数为MAIN PID
,也就是 fork 子进程启动后的PID
)。 - systemd 无法获取
PID 31036
,不存在或者为僵尸进程。
先来检查进程 ID
与 mysqld.pid
看看:
确认线索:
PID 31036
不存在mysqld.pid
文件存在,且文件内容为 31036top
命令查看不存在僵尸进程
还需要获取更多的线索来确认原因,检查 journalctl -u
内容,看看是否有帮助:
sh-4.4# journalctl -u mysqld_11690.service -- Logs begin at Mon 2024-02-05 04:00:35 CST, end at Mon 2024-02-05 17:08:01 CST. -- Feb 05 05:07:54 udp-11 systemd[1]: Starting MySQL Server... Feb 05 05:07:56 udp-11 systemd[1]: Started MySQL Server. Feb 05 05:08:31 udp-11 systemd[1]: mysqld_11690.service: Main process exited, code=killed, status=9/KILL Feb 05 05:08:31 udp-11 systemd[1]: mysqld_11690.service: Failed with result 'signal'. Feb 05 05:08:32 udp-11 systemd[1]: Starting MySQL Server... Feb 05 05:08:36 udp-11 systemd[1]: Started MySQL Server. Feb 05 05:08:37 udp-11 systemd[1]: mysqld_11690.service: Main process exited, code=killed, status=9/KILL Feb 05 05:08:37 udp-11 systemd[1]: mysqld_11690.service: Failed with result 'signal'. Feb 05 05:08:39 udp-11 systemd[1]: Starting MySQL Server... Feb 05 05:08:42 udp-11 u_set_iops.sh[31507]: /etc/systemd/system/mysqld_11690.service.d/u_set_iops.sh: option requires an argument -- p Feb 05 05:08:42 udp-11 systemd[1]: mysqld_11690.service: New main PID 31036 does not exist or is a zombie.
这里的 journalctl -u
内容也只描述了现象,无法分析具体原因,与 systemctl status 的内容相差不多,帮助不大。
查看 /var/log/messages
系统日志内容:
发现循环报出了一些内存方面的错误信息,通过搜索后发现该错误可能为硬件问题。询问了自动化测试的同事后,得到结论:
- 场景为偶发问题,执行 4 次用例,2 次成功,2 次失败
- 每次执行均为同一台宿主机,同一份容器镜像
- 失败时 hang 住的容器为同一个
既然有成功执行的结果,这里就先忽略硬件问题导致的。
既然提到了容器,此时想到了 cgroup 会不会映射宿主机的时候出现了问题?在上边排查的 systemctl status 中,观察可知 cgroup 映射的宿主机目录为:CGroup: /docker/3a72b2cdc7bd9beb1c7b2abec24763046604602a38f0fcb7406d17f5d33353d2/system.slice/mysqld_11690.service
检查父级文件夹 system.slice
的读写权限并无异常。先暂时排除 cgroup 的映射问题(因为主机上还有其他 systemd 接管的 service 也在使用同一份 cgroup)。
打算试试 pstack 能不能看到 systemd 具体 hang 在了哪个地方,3048143
为 systemctl start 的 pid
:
sh-4.4# pstack 3048143 #0 0x00007fdfaef33ade in ppoll () from /lib64/libc.so.6 #1 0x00007fdfaf7768ee in bus_poll () from /usr/lib/systemd/libsystemd-shared-239.so #2 0x00007fdfaf6a8f3d in bus_wait_for_jobs () from /usr/lib/systemd/libsystemd-shared-239.so #3 0x000055b4c2d59b2e in start_unit () #4 0x00007fdfaf7457e3 in dispatch_verb () from /usr/lib/systemd/libsystemd-shared-239.so #5 0x000055b4c2d4c2b4 in main ()
观察发现 start_unit 比较可疑,start_unit()
函数位于可执行文件中,它用于启动 systemd units,并没有什么帮助。
根据已有线索,推测后可知:
mysqld.pid
文件存在,则表示之前确实有一个 mysqld 且进程号为31036
的进程被启动了- 进程启动后被自动化用例
kill -9
结束掉 - systemd 获取到了一个已经被结束的
MAIN PID
,后置 shell 执行失败,fork 流程失败
通过梳理 systemd 启动流程的步骤,推测可能性。MySQL 实例只有在 mysqld 成功启动后才会生成 mysqld.pid
文件,所以可能是在后续步骤里被意外 kill -9
结束掉导致的。
复现方式
既然没什么其他头绪和线索了,打算根据推测结论尝试复现一下试试。
4.1 调整 systemd mysql serivce 模板
编辑模板文件 /etc/systemd/system/mysqld_11690.service
,在 mysqld 启动后,sleep10
秒,方便在这时间窗口内模拟kill掉实例进程的场景。
4.2 配置重载
执行 systemctl daemon-reload
令变更生效。
4.3 场景重现
- [ssh seesion A] 首先准备一个新的容器,做好相关配置后执行
sudo -S systemctl start mysqld_11690.service
启动一个 mysqld 进程,此时会因为sleep
的原因 hang 住会话。 - [ssh seesion B] 在另一个会话窗口,
start
命令 hang 住时,检查mysqld.pid
文件,一旦文件被创建后,立刻执行sudo -S kill -9 $(cat /opt/mysql/data/11690/mysqld.pid)
。 - 此时观察 systemctl status,表现与预期一致
解决方式
先 kill
掉 hang 住的 systemctl start 命令,执行 systemctl stop mysqld_11690.service
,这可以让 systemd 主动结束僵尸进程,虽然 stop
命令可能会报错但这并不影响。
等待 stop
执行完成后再次使用 start
命令启动,恢复正常。
更多技术文章,请访问:https://opensource.actionsky.com/
关于 SQLE
SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。
SQLE 获取

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
得物 ZooKeeper SLA 也可以 99.99%
1. 背景 ZooKeeper(ZK)是一个诞生于 2007 年的分布式应用程序协调服务。尽管出于一些特殊的历史原因,许多业务场景仍然不得不依赖它。比如,Kafka、任务调度等。特别是在 Flink 混合部署 ETCD 解耦 时,业务方曾要求绝对的稳定性,并强烈建议不要使用自建的 ZooKeeper。出于对稳定性的考量,采用了阿里的 MSE-ZK。自从 2022 年 9 月份开始使用至今,得物技术团队没有遇到任何稳定性问题,SLA 的可靠性确实达到了 99.99%。 在 2023 年,部分业务使用了自建的 ZooKeeper(ZK)集群,然后使用过程中 ZK 出现了几次波动,随后得物 SRE 开始接管部分自建集群,并进行了几轮稳定性加固的尝试。接管过程中得物发现 ZooKeeper 在运行一段时间后,内存占用率会不断增加,容易导致内存耗尽(OOM)的问题。得物技术团队对这一现象非常好奇,因此也参与了解决这个问题的探索过程。 2. 探索分析 2.1 确定方向 在排查问题时,非常幸运地发现了一个测试环境的故障现场,该集群中的两个节点恰好处于 OOM 的边缘状态。 有了故障现场,那么一般情况...
- 下一篇
已经有 Prometheus 了,还需要夜莺?
谈起当下监控,Prometheus 无疑是最火的项目,如果只是监控机器、网络设备,Zabbix 尚可一战,如果既要监控设备又要监控应用程序、Kubernetes 等基础设施,Prometheus 就是最佳选择。甚至有些开源项目,已经内置支持了 Prometheus 协议的指标暴露,比如新版本的 Zookeeper、新版本的 RabbitMQ、Nginx vts 等等。Prometheus 的影响力可见一斑。 很多场景里讲到的 Prometheus 这个词,其实已经不仅仅是 Prometheus 项目本身了,而是 Prometheus 生态,包括 Prometheus 定义的指标格式、传输协议、查询语言、各类 Exporter 采集器、各类兼容的存储等。 在 Prometheus 生态里,采集可以使用各类 Exporter,存储可以使用 VictoriaMetrics,看图可以使用 Grafana,看起来已经非常完备了,为啥又冒出一个“夜莺(Nightingale)”的开源项目,还声称和 Prometheus 无缝对接?本文尝试探讨一二。 夜莺介绍 从夜莺官网摘出一段夜莺项目介绍: 夜莺...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 2048小游戏-低调大师作品
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,CentOS7官方镜像安装Oracle11G