1. 背景说明
在配置 Percona MySQL 5.7.22 自启时,直接通过 systemd 调用
mysqld_safe容易出现进程误判、无限重启、进程堆积等问题。而 Percona 官方提供的/etc/init.d/mysql.server脚本已内置对 mysqld_safe 启动逻辑、PID 文件管理、进程状态校验的完整适配,基于该脚本配置 systemd 服务更稳定、更贴合 MySQL 原生启动逻辑,也是最终验证可行的方案。2. 环境信息确认
/etc/init.d/mysql.server 内容
[root@wx-bigdata-ceshi-jili-178-235 mysql3306]# cat /etc/init.d/mysql.server #!/bin/sh # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind # MySQL (Percona Server) daemon start/stop script. # Usually this is put in /etc/init.d (at least on machines SYSV R4 based # systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql. # When this is done the mysql server will be started when the machine is # started and shut down when the systems goes down. # Comments to support chkconfig on RedHat Linux # chkconfig: 2345 64 36 # description: A very fast and reliable SQL database engine. # Comments to support LSB init script conventions ### BEGIN INIT INFO # Provides: mysql # Required-Start: $local_fs $network $remote_fs # Should-Start: ypbind nscd ldap ntpd xntpd # Required-Stop: $local_fs $network $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: start and stop MySQL (Percona Server) # Description: Percona-Server is a SQL database engine with focus on high performance. ### END INIT INFO # If you install MySQL on some other places than /usr/local/Percona-Server-5.7.22-22-Linux.x86_64.ssl101, then you # have to do one of the following things for this script to work: # # - Run this script from within the MySQL installation directory # - Create a /etc/my.cnf file with the following information: # [mysqld] # basedir=<path-to-mysql-installation-directory> # - Add the above to any other configuration file (for example ~/.my.ini) # and copy my_print_defaults to /usr/bin # - Add the path to the mysql-installation-directory to the basedir variable # below. # # If you want to affect other MySQL variables, you should make your changes # in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files. # If you change base dir, you must also change datadir. These may get # overwritten by settings in the MySQL configuration files. basedir=/usr/local/mysql datadir=/data/mysql/mysql3306/data mycnf=/etc/my.cnf # Default value, in seconds, afterwhich the script should timeout waiting # for server start. # Value here is overriden by value in my.cnf. # 0 means don't wait at all # Negative numbers mean to wait indefinitely service_startup_timeout=900 # Lock directory for RedHat / SuSE. lockdir='/var/lock/subsys' lock_file_path="$lockdir/mysql" # The following variables are only set for letting mysql.server find things. # Set some defaults mysqld_pid_file_path= if test -z "$basedir" then basedir=/usr/local/Percona-Server-5.7.22-22-Linux.x86_64.ssl101 bindir=/usr/local/Percona-Server-5.7.22-22-Linux.x86_64.ssl101/bin if test -z "$datadir" then datadir=/usr/local/Percona-Server-5.7.22-22-Linux.x86_64.ssl101/data fi sbindir=/usr/local/Percona-Server-5.7.22-22-Linux.x86_64.ssl101/bin libexecdir=/usr/local/Percona-Server-5.7.22-22-Linux.x86_64.ssl101/bin else bindir="$basedir/bin" if test -z "$datadir" then datadir="$basedir/data" fi sbindir="$basedir/sbin" libexecdir="$basedir/libexec" fi # datadir_set is used to determine if datadir was set (and so should be # *not* set inside of the --basedir= handler.) datadir_set= # # Use LSB init script functions for printing messages, if possible # lsb_functions="/lib/lsb/init-functions" if test -f $lsb_functions ; then . $lsb_functions else log_success_msg() { echo " SUCCESS! $@" } log_failure_msg() { echo " ERROR! $@" } fi PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin" export PATH mode=$1 # start or stop [ $# -ge 1 ] && shift other_args="$*" # uncommon, but needed when called from an RPM upgrade action # Expected: "--skip-networking --skip-grant-tables" # They are not checked here, intentionally, as it is the resposibility # of the "spec" file author to give correct arguments only. case `echo "testing\c"`,`echo -n testing` in *c*,-n*) echo_n= echo_c= ;; *c*,*) echo_n=-n echo_c= ;; *) echo_n= echo_c='\c' ;; esac parse_server_arguments() { for arg do case "$arg" in --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'` bindir="$basedir/bin" if test -z "$datadir_set"; then datadir="$basedir/data" fi sbindir="$basedir/sbin" libexecdir="$basedir/libexec" ;; --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'` datadir_set=1 ;; --pid-file=*) mysqld_pid_file_path=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; esac done } wait_for_pid () { verb="$1" # created | removed pid="$2" # process ID of the program operating on the pid-file pid_file_path="$3" # path to the PID file. i=0 avoid_race_condition="by checking again" while test $i -ne $service_startup_timeout ; do case "$verb" in 'created') # wait for a PID-file to pop into existence. test -s "$pid_file_path" && i='' && break ;; 'removed') # wait for this PID-file to disappear test ! -s "$pid_file_path" && i='' && break ;; *) echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path" exit 1 ;; esac # if server isn't running, then pid-file will never be updated if test -n "$pid"; then if kill -0 "$pid" 2>/dev/null; then : # the server still runs else # The server may have exited between the last pid-file check and now. if test -n "$avoid_race_condition"; then avoid_race_condition="" continue # Check again. fi # there's nothing that will affect the file. log_failure_msg "The server quit without updating PID file ($pid_file_path)." return 1 # not waiting any more. fi fi echo $echo_n ".$echo_c" i=`expr $i + 1` sleep 1 done if test -z "$i" ; then log_success_msg return 0 else log_failure_msg return 1 fi } # Get arguments from the my.cnf file, # the only group, which is read from now on is [mysqld] if test -x "$bindir/my_print_defaults"; then print_defaults="$bindir/my_print_defaults" else # Try to find basedir in /etc/my.cnf conf=/etc/my.cnf print_defaults= if test -r $conf then subpat='^[^=]*basedir[^=]*=\(.*\)$' dirs=`sed -e "/$subpat/!d" -e 's//\1/' $conf` for d in $dirs do d=`echo $d | sed -e 's/[ ]//g'` if test -x "$d/bin/my_print_defaults" then print_defaults="$d/bin/my_print_defaults" break fi done fi # Hope it's in the PATH ... but I doubt it test -z "$print_defaults" && print_defaults="my_print_defaults" fi # # Read defaults file from 'basedir'. If there is no defaults file there # check if it's in the old (depricated) place (datadir) and read it from there # extra_args="" if test -r "$basedir/my.cnf" then extra_args="-e $basedir/my.cnf" fi parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` # # Set pid file if not given # if test -z "$mysqld_pid_file_path" then mysqld_pid_file_path=$datadir/`hostname`.pid else case "$mysqld_pid_file_path" in /* ) ;; * ) mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;; esac fi case "$mode" in 'start') # Start daemon # Safeguard (relative paths, core dumps..) cd $basedir echo $echo_n "Starting MySQL (Percona Server)" if test -x $bindir/mysqld_safe then # Give extra arguments to mysqld with the my.cnf file. This script # may be overwritten at next upgrade. $bindir/mysqld_safe --defaults-file="$mycnf" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null & wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$? # Make lock for RedHat / SuSE if test -w "$lockdir" then touch "$lock_file_path" fi exit $return_value else log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)" fi ;; 'stop') # Stop daemon. We use a signal here to avoid having to know the # root password. if test -s "$mysqld_pid_file_path" then # signal mysqld_safe that it needs to stop touch "$mysqld_pid_file_path.shutdown" mysqld_pid=`cat "$mysqld_pid_file_path"` if (kill -0 $mysqld_pid 2>/dev/null) then echo $echo_n "Shutting down MySQL (Percona Server)" kill $mysqld_pid # mysqld should remove the pid file when it exits, so wait for it. wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$? else log_failure_msg "MySQL (Percona Server) server process #$mysqld_pid is not running!" rm "$mysqld_pid_file_path" fi # Delete lock for RedHat / SuSE if test -f "$lock_file_path" then rm -f "$lock_file_path" fi exit $return_value else log_failure_msg "MySQL (Percona Server) PID file could not be found!" fi ;; 'restart') # Stop the service and regardless of whether it was # running or not, start it again. if $0 stop $other_args; then $0 start $other_args else log_failure_msg "Failed to stop running server, so refusing to try to start." exit 1 fi ;; 'reload'|'force-reload') if test -s "$mysqld_pid_file_path" ; then read mysqld_pid < "$mysqld_pid_file_path" kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL (Percona Server)" touch "$mysqld_pid_file_path" else log_failure_msg "MySQL (Percona Server) PID file could not be found!" exit 1 fi ;; 'status') # First, check to see if pid file exists if test -s "$mysqld_pid_file_path" ; then read mysqld_pid < "$mysqld_pid_file_path" if kill -0 $mysqld_pid 2>/dev/null ; then log_success_msg "MySQL (Percona Server) running ($mysqld_pid)" exit 0 else log_failure_msg "MySQL (Percona Server) is not running, but PID file exists" exit 1 fi else # Try to find appropriate mysqld process mysqld_pid=`pidof $libexecdir/mysqld` # test if multiple pids exist pid_count=`echo $mysqld_pid | wc -w` if test $pid_count -gt 1 ; then log_failure_msg "Multiple MySQL running but PID file could not be found ($mysqld_pid)" exit 5 elif test -z $mysqld_pid ; then if test -f "$lock_file_path" ; then log_failure_msg "MySQL (Percona Server) is not running, but lock file ($lock_file_path) exists" exit 2 fi log_failure_msg "MySQL (Percona Server) is not running" exit 3 else log_failure_msg "MySQL (Percona Server) is running but PID file could not be found" exit 4 fi fi ;; *) # usage basename=`basename "$0"` echo "Usage: $basename {start|stop|restart|reload|force-reload|status} [ MySQL (Percona Server) options ]" exit 1 ;; esac exit 0
先确认当前 MySQL 环境核心参数(从
/etc/init.d/mysql.server脚本中提取),确保配置精准匹配:| 配置项 | 实际路径 / 值 |
|---|---|
| MySQL 安装目录(basedir) | /usr/local/mysql |
| 数据目录(datadir) | /data/mysql/mysql3306/data |
| 核心配置文件(my.cnf) | /etc/my.cnf |
| init.d 启动脚本 | /etc/init.d/mysql.server |
| PID 文件路径 | /data/mysql/mysql3306/data/[主机名].pid(脚本自动适配) |
| 服务启动超时时间 | 900 秒(脚本内置,适配大内存 / 大数据量场景) |
3. 核心原理
/etc/init.d/mysql.server是 Percona 官方适配的启动脚本,内置以下关键能力,避免手动配置的缺陷:- 自动解析
/etc/my.cnf中的 basedir、datadir、pid-file 等核心配置,无需手动指定; - 内置
wait_for_pid函数,精准校验 MySQL 启动 / 停止状态,避免 systemd 误判进程退出; - 自动管理 PID 文件和锁文件(
/var/lock/subsys/mysql),防止进程重复启动; - 兼容
mysqld_safe的 fork 启动逻辑,与 systemd 的Type=forking模式完美匹配。
4. 详细配置步骤
步骤 1:确认官方 init.d 脚本有效性
首先验证
/etc/init.d/mysql.server脚本可正常使用(已存在且可执行):# 检查脚本是否存在
ls -l /etc/init.d/mysql.server
# 赋予执行权限(如无)
chmod +x /etc/init.d/mysql.server
# 测试脚本启动/停止功能(验证基础可用性)
/etc/init.d/mysql.server start
/etc/init.d/mysql.server stop
步骤 2:编写 systemd 服务文件
创建 / 修改
/usr/lib/systemd/system/mysql.service,复用官方 init.d 脚本:vi /usr/lib/systemd/system/mysql.service
粘贴以下完整配置(包含开机自启 + 异常自启,补充原配置缺失的异常重启能力):
[Unit]
# 服务描述
Description=Percona MySQL 5.7.22 (基于官方init.d脚本)
# 启动依赖:网络就绪后启动
After=network.target
[Service]
# 启动模式:适配init.d脚本的fork特性
Type=forking
# 调用官方脚本启动(复用原生启动逻辑)
ExecStart=/etc/init.d/mysql.server start
# 调用官方脚本停止(优雅关闭,避免进程残留)
ExecStop=/etc/init.d/mysql.server stop
# 核心:异常自启配置(进程崩溃/被杀死时自动重启)
Restart=on-failure
# 异常重启间隔:5秒,防止频繁重启
RestartSec=5
# 私有临时目录,提升安全性
PrivateTmp=true
# 放开文件句柄限制(匹配my.cnf配置)
LimitNOFILE=65535
# 仅杀死主进程,避免误杀子进程
KillMode=process
[Install]
# 开机启动级别:多用户模式(系统默认)
WantedBy=multi-user.target
步骤 3:加载配置并启用自启
# 1. 重新加载systemd配置(使新服务文件生效)
systemctl daemon-reload
# 2. 设置开机自启(核心:开机时自动启动mysql.service)
systemctl enable mysql.service
# 3. 启动MySQL服务(立即生效)
systemctl start mysql.service
# 4. 查看服务状态(正常应显示 active (running))
systemctl status mysql.service
4. 功能验证
验证 1:基础启动验证
# 1. 查看服务状态(显示active (running)则启动成功)
systemctl status mysql.service
# 2. 验证MySQL可正常连接
mysql -P3306 -uroot -pmysql -h 172.172.178.235 -uinspire -pinspire123 --default-character-set=utf8
# 3. 查看进程(仅1组mysqld/mysqld_safe进程,无堆积)
ps -ef | grep mysqld | grep -v grep
验证 2:异常自启验证(核心)
模拟 MySQL 进程异常崩溃,验证自动重启能力:
# 1. 获取MySQL主进程PID(从脚本管理的PID文件读取)
MYSQL_PID=$(cat /data/mysql/mysql3306/data/$(hostname).pid)
# 2. 强制杀死MySQL进程(模拟异常退出)
kill -9 $MYSQL_PID
# 3. 等待5秒(匹配RestartSec=5)
sleep 5
# 4. 检查进程是否自动重启(有新PID,且仅1组进程)
ps -ef | grep mysqld | grep -v grep
# 5. 验证MySQL可正常连接(重启后服务可用)
mysql -P3306 -uroot -pmysql -h 172.172.178.235 -uinspire -pinspire123 --default-character-set=utf8
验证 3:开机自启验证
# 检查开机自启状态,输出"enabled"则配置成功
systemctl is-enabled mysql.service
# 可选:重启服务器后验证(生产环境谨慎操作)
reboot
# 重启后执行,验证服务自动启动
systemctl status mysql.service
5. 常用管理命令
配置完成后,可通过以下命令便捷管理 MySQL 服务:
# 启动服务
systemctl start mysql.service
# 停止服务
systemctl stop mysql.service
# 重启服务
systemctl restart mysql.service
# 查看服务状态
systemctl status mysql.service
# 关闭开机自启(如需)
systemctl disable mysql.service
# 查看MySQL运行状态(通过官方脚本)
/etc/init.d/mysql.server status
# 重新加载MySQL配置(无需重启服务)
/etc/init.d/mysql.server reload
6. 关键配置说明
| systemd 配置项 | 作用 |
|---|---|
Type=forking |
适配 init.d 脚本的后台 fork 启动逻辑,systemd 可精准识别启动状态 |
Restart=on-failure |
仅在进程异常退出(崩溃、被 kill、启动失败)时重启,避免正常停止触发重启 |
RestartSec=5 |
异常退出后延迟 5 秒重启,防止短时间内频繁重启耗尽系统资源 |
LimitNOFILE=65535 |
放开文件句柄限制,匹配 my.cnf 中open-files-limit=65535的配置 |
7. 总结
- 基于 Percona 官方
/etc/init.d/mysql.server脚本配置 systemd 服务,是适配 MySQL 5.7.22 自启的最优方案,避免了直接调用mysqld_safe的进程误判、堆积等问题; - 核心配置需包含
Type=forking(适配脚本启动逻辑)+Restart=on-failure(实现异常自启),兼顾稳定性和自启需求; - 最终配置可实现:开机自动启动 MySQL、进程异常崩溃后自动重启,且服务状态正常、无进程堆积,完全满足生产环境自启需求。
posted on
浙公网安备 33010602011771号