用systemd管理GreatSQL服务详解

用systemd管理GreatSQL服务详解

1.GreatSQL服务文件

官网 greatsql.serviC++e 文件

[Unit]
Description=GreatSQL Server
Documentation=man:MySQLd(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.HTML
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]

# 本文省略some limits

User=mysql
Group=mysql
Type=notify
TimeoutSec=10
PermissionsStartOnly=true
ExecStartPre=/usr/local/GreatSQL-8.0.32-27-Linux-glibc2.28-x86_64/bin/mysqld_pre_systemd
ExecStart=/usr/local/GreatSQL-8.0.32-27-Linux-glibc2.28-x86_64/bin/mysqld $MYSQLD_OPTS
EnvironmentFile=-/etc/sysconfig/mysql
Restart=on-failure
RestartPreventExitStatus=1
Environment=MYSQLD_PARENT_PID=1
PrivateTmp=false

上述服务文件中 MYSQLD_OPTSMYSQLD_PARENT_PID 的用途是什么?TypeExecStart 有什么关系?服务停止的逻辑是什么?TimeoutSec 超时会怎样?

2.环境变量

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql

2.1 MYSQLD_OPTS

MYSQLD_OPTS 是一个特殊的环境变量,用于在启动时向 MYSQLD 进程传递额外的命令行参数。适合需要动态调整参数的场景。

可以通过以下方式设置 MYSQLD_OPTS

  1. systemctl,全局环境变量
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
  1. 服务文件,单个服务环境变量
[Service]
Environment=MYSQLD_OPTS=--general_log=1
EnvironmentFile=-/data/conf/greatsql

2.2 Environment

  1. 服务文件中设置 Environment
[Service]
Environment=LD_PRELOAD=/usr/local/jemalloc-5.3.0/lib/libjemalloc.so
Environment=LD_PRELOAD=/data/svr/greatsql/lib/mysql/libjemalloc.so  #覆盖之前同名的变量
Environment=  #清空所有环境变量

如果同一变量被重复设置,后续的赋值会覆盖之前的值。如果将此选项赋值为空字符串,则会重置环境变量列表,之前的所有设置均失效。

  1. 服务文件中设置 EnvironmentFile
[Service]
EnvironmentFile=-/etc/sysconfig/mysql  #-表示忽略文件不存在错误
EnvironmentFile=-/data/conf/greatsql
EnvironmentFile=  #清空所有待读取的文件

$ cat /data/conf/greatsql
LD_PRELOAD=/data/svr/greatsql/lib/mysql/libjemalloc.so
LD_LIBRARY_PATH=/data/svr/greatsql/lib
TZ=CST
MYSQLD_OPTS=--general_log=1 --port=4307

EnvironmentFile 可以设置多次,所有匹配的文件均会被读取。若将此选项赋值为空字符串,则会清空待读取的文件列表,之前所有设置均失效。

EnvironmentFile 按顺序依次读取,后加载的变量会覆盖之前的设定,且会覆盖 Environment 中的同名变量。

Environment、EnvironmentFile 在服务启动前解析,这些变量会被直接写入服务的环境变量列表,对所有后续命令(ExecStartPre、ExecStart、ExecStartPost)可见。

如果 EnvironmentFile 指定的文件在运行时动态生成,systemd 会尝试读取它,如果文件在读取时被修改,systemd 会使用最新的内容。

3.启动

systemd 通过 fork-exec + cgroups 的机制创建并严格管理服务进程,确保所有进程均为其子进程。

  • Uses fork() + execve() to spawn the new process:
    • fork(): Creates a child process (a copy of the systemd parent).
    • execve(): Overwrites the child process with the target binary.
  • Assigns the process to a dedicated cgroup
    • Ensures all child processes remain within the same cgroup.
    • Enables resource limits and process tracking.

3.1 Type=simple

[Service]
Type=simple
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS

3.1.1 行为

  • 要求 ExecStart 启动的是前台命令其将作为服务的主进程(Main Process)
  • ExecStart 进程创建即启动成功(即使进程还在初始化或监听端口未就绪);如果进程崩溃或退出,systemd 会根据 Restart= 规则决定是否重启
  • 适用于不 fork() 且不依赖其他进程的服务

3.1.2 错误示例

如果 ExecStart 启动的命令以 daemon 模式运行,daemon 进程有一个瞬间退出的中间父进程,对应就是子进程。在子进程退出时,systemd 会将其从监控队列中踢掉,同时杀掉所有附属进程(杀进程的方式由 KillMode 控制)。

# KillMode=control-group
$ systemctl start db-4306
$ systemctl status db-4306
● db-4306.service - db-4306 Server
   Loaded: loaded (/usr/lib/systemd/system/db-4306.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Fri 2025-05-30 11:03:26 CST; 9s aGo
  Process: 1914 ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf --daemonize $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
 Main PID: 1914 (code=exited, status=0/SUCCESS)

Jun 05 11:03:22 dbcluster-165 systemd[1]: Started db-4306 Server.
$ ps aux |grep 4306 |grep -v grep

Type=simple,执行 daemon 命令,默认启动后马上会停止。

3.2 Type=forking

[Service]
Type=forking
PIDFile=/data/dbdata/data4306/data/mysql.pid
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf --daemonize $MYSQLD_OPTS

3.2.1 行为

  • 要求 ExecStart 启动的命令以 daemon 模式运行,服务预期自行 fork() 并退出
  • ExecStart 进程 fork() 出的进程将作为服务的主进程(Main Process),推荐设置 PIDFile 用以正确监控服务主进程,否则通过 cgroup 跟踪
  • PIDFile 只适合在 Type=forking 模式下使用,如果有设置 PIDFile,systemd 会在 ExecStart 进程退出后立即读取这个 PIDFile,读取成功后就认为该服务已经启动成功,读取失败就认为该服务启动失败
  • 适用于传统 Unix 守护进程

以下是 forking 模式下正常启动的服务

$ systemctl status db-4306
● db-4306.service - db-4306 Server
   Loaded: loaded (/usr/lib/systemd/system/db-4306.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2025-05-29 22:28:03 CST; 11s ago
  Process: 24262 ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf --daemonize $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
 Main PID: 24342 (mysqld)
    Tasks: 54
   CGroup: /system.slice/db-4306.service
           └─24342 /data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf --daemonize

May 29 22:28:01 dbcluster-165 systemd[1]: Starting db-4306 Server...
May 29 22:28:03 dbcluster-165 systemd[1]: Started db-4306 Server.

ExecStart 启动的进程 PID=24262,且该进程的状态是已退出,退出状态码为0,这个进程是 daemon 类进程创建过程中瞬间退出的中间父进程。Main PID: 24342 (MySQLd),这是 systemd 真正监控的服务主进程。

3.2.2 错误示例

如果 ExecStart 是一个前台命令,systemd 会一直等待 ExecStart 启动的进程作为中间父进程退出,在等待过程中,systemctl start 会一直卡住,直到等待超时而失败。

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
0

Type=forking,执行前台命令,在Restart=on-failure场景,启动超时导致服务反复重启。

3.3 Type=notify

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
1

3.3.1 行为

  • 类似 simple,要求 ExecStart 启动的是前台命令其将作为服务的主进程(Main Process)
  • 进程支持 sd_notify(),必须正确配置 NotifyAccess 和超时时间
  • 进程在启动完成、状态更新、停止通知后必需主动通过 sd_notify() 向 systemd 发送通知
  • 适用于实现更精确的启动、运行和停止管理服务

3.4 mysqld显示更多变量

当使用 mysqld_safe 启动数据库时,ps 可以看到 mysqld 进程带有很多变量

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
2

mysqld_safe 处理逻辑如下

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
3

对于 systemd service,可以添加 ExecStartPre,从 --defaults-file 中获取需要显示的变量

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
4

3.5 mysqld先于磁盘挂载启动

对于开机自动启动,如果磁盘挂载服务启动较慢,数据库服务可能会报错,可以配置数据库服务延迟启动

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
5

服务日志如下

[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
6

systemctl status中 ERROR 的原因:执行 ExecStartPre 会加载服务的环境变量,此时由于磁盘挂载暂未完成,导致 so 文件无法加载(这一条 ERROR 可以忽略)。只要延迟足够,在磁盘挂载完成后再执行 ExecStart,就能正常加载配置的 so 文件。

4.停止

4.1 systemctl stop 逻辑

  1. 执行 ExecStop (设置了才执行,否则进入第2步)
    1. ExecStop 成功终止进程,systemd 检测到进程已退出,不再发送 SIGTERM
    2. ExecStop 未终止进程,例如 ExecStop 超时(TimeoutStopSec),ExecStop 命令与停止进程无关,进入第2步
  2. 发送 SIGTERM(默认,KillSignal=SIGTERM),优雅退出
    1. SIGTERM 成功终止进程,服务退出
    2. SIGTERM 未终止进程,例如 SIGTERM 超时(TimeoutStopSec),进入第3步
  3. 发送 SIGKILL(默认,SendSIGKILL=yes),强制终止

4.2 systemctl stop 执行超时

对于版本升级等场景,通常会设置 innodb_fast_shutdown=0,此时关闭数据库会比较慢,如果 TimeoutStopSec 过小,可能导致 ExecStop、SIGTERM 超时,触发 SIGKILL。

  1. 数据库以 systemd 服务运行
[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
7
  1. ExecStop 超时,SIGTERM 超时,对应的服务日志
[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
8

systemctl stop 超时(ExecStop、SIGTERM),服务会被 SIGKILL。systemd 认为是一个预期的行为,不会触发重启。

4.3 命令行执行 shutdown 超时

  1. 数据库以 systemd 服务运行(同上)
  2. 命令行执行 shutdown
[Service]
ExecStart=/data/svr/greatsql/bin/mysqld --defaults-file=/data/conf/greatsql4306.cnf $MYSQLD_OPTS
EnvironmentFile=-/data/conf/greatsql
9

systemd 对所有通过它管理的服务都实施完整的生命周期控制。当执行 shutdown 时,信号传递链:shutdown --> 数据库服务 --> systemd 服务管理器。systemd 会监控整个停止过程。

  1. 对应服务日志
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
0

SIGTERM 超时,发送 SIGKILL 强制终止,进程退出原因是 Unclean signal。在 Restart=on-failure 场景,等待 RestartSec(默认100ms)后重启。

命令行 shutdown 超时(SIGTERM),服务会被 SIGKILL,之后会触发重启。

4.4 禁用 systemd 的默认信号

如果要禁用 systemd 的默认停止行为,可以参考如下设置

# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
1

5.重启

Configures whether the service shall be restarted when the service process exits, is killed, or a timeout is reached. The service process may be the main service process, but it may also be one of the processes specified with ExecStartPre=, ExecStartPost=, ExecStop=, ExecStopPost=, or ExecReload=. When the death of the process is a result of systemd operation (e.g. service stop or restart), the service will not be restarted. Timeouts include missing the watchdog "keep-alive ping" deadline and a service start, reload, and stop operation timeouts.

5.1 Restart 的取值

Restart settings/Exit causes no always on-success on-failure on-abnormal on-abort on-watchdog
Clean exit code or signal X X
Unclean exit code X X
Unclean signal X X X X
Timeout X X X
Watchdog X X X X

A clean exit means an exit code of 0, or one of the signals SIGHUP, SIGINT, SIGTERM or SIGPIPE, and additionally, exit statuses and signals specified in SuccessExitStatus=.

5.2 命令行执行 restart 无法重启

  1. 数据库以 systemd 服务运行(同上)
  2. 命令行执行 restart
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
2

原因:没有设置相关的监控进程(https://dev.mysql.com/doc/refman/8.0/en/restart.html)

  1. 查看运行中数据库进程的 MYSQLD_PARENT_PID
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
3
  1. 设置 MYSQLD_PARENT_PID
  • mysqld_safe,默认将 mysqld_safe 的PID设置为监控进程
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
4
  • systemd,需在服务文件添加
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
5
  1. 重启数据库生效
# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
6

如果要求不重启数据库就能执行 restart 命令,可通过 gdb 动态修改环境变量,gdb 会短时间阻塞数据库

# 设置
systemctl set-environment MYSQLD_OPTS="--general_log=1"
# 取消
systemctl unset-environment MYSQLD_OPTS
7

clone 同样需要设置 MYSQLD_PARENT_PID,才能自动重启。

5.3相关参数

ReStartSec:重启条件满足后等多久自动重启

StartLimitInterval、StartLimitBurst:限制指定时间内(StartLimitInterval)重启的次数(StartLimitBurst)

RestartPreventExitStatus:指定某些退出状态码或信号不重启。GreatSQL服务建议设置为1,在遇到严重错误时不重启实例,需人工介入处理

RestartForceExitStatus:强制将某些退出状态码或信号重启。比如 Restart=no,RestartForceExitStatus=16,则不依赖自动重启,但命令行执行 restart 可正常重启实例

6.总结

  • 合理设置 Type 和 ExecStart
  • 建议设置一个较大的 TimeoutStopSec,避免 ExecStop 或者 SIGTERM 超时
  • Clone 和命令行 restart 需要设置 MYSQLD_PARENT_PID
  • 建议设置 ReStartSec、StartLimitInterval 和 StartLimitBurst 限制重启频率
  • 可以设置 ExecStartPre和EnvironmentFile,添加 ps 需要显示的变量

Enjoy GreatSQL

关于 GreatSQL

GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

社区博客有奖征稿详情:https://greatsql.cn/thread-100-1-1.html

技术交流群:

微信:扫码添加GreatSQL社区助手微信好友,发送验证信息加群