• 售前

  • 售后

热门帖子
入门百科

深度剖析MySQL启动时报“The server quit without updating PID file”错误

[复制链接]
123457018 显示全部楼层 发表于 2021-10-25 19:37:11 |阅读模式 打印 上一主题 下一主题
很多童鞋在启动mysql的时间,遇到过这个错误,
起首,澄清一点,出现这个错误的条件是:通过服务脚原来启动mysql。通过mysqld_safe或mysqld启动mysql实例并不会报这个错误。
那么,出现这个错误的原因具体是什么呢?
哈哈,对分析过程不care的童鞋可直接跳到文末的总结部分~
总结
下面,来分析下mysql的服务启动脚本
脚本完备内容如下:
  1. #!/bin/sh
  2. # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
  3. # This file is public domain and comes with NO WARRANTY of any kind
  4. # MySQL daemon start/stop script.
  5. # Usually this is put in /etc/init.d (at least on machines SYSV R4 based
  6. # systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql.
  7. # When this is done the mysql server will be started when the machine is
  8. # started and shut down when the systems goes down.
  9. # Comments to support chkconfig on RedHat Linux
  10. # chkconfig: 2345 64 36
  11. # description: A very fast and reliable SQL database engine.
  12. # Comments to support LSB init script conventions
  13. ### BEGIN INIT INFO
  14. # Provides: mysql
  15. # Required-Start: $local_fs $network $remote_fs
  16. # Should-Start: ypbind nscd ldap ntpd xntpd
  17. # Required-Stop: $local_fs $network $remote_fs
  18. # Default-Start: 2 3 4 5
  19. # Default-Stop: 0 1 6
  20. # Short-Description: start and stop MySQL
  21. # Description: MySQL is a very fast and reliable SQL database engine.
  22. ### END INIT INFO
  23. # If you install MySQL on some other places than /usr/local/mysql, then you
  24. # have to do one of the following things for this script to work:
  25. #
  26. # - Run this script from within the MySQL installation directory
  27. # - Create a /etc/my.cnf file with the following information:
  28. # [mysqld]
  29. # basedir=<path-to-mysql-installation-directory>
  30. # - Add the above to any other configuration file (for example ~/.my.ini)
  31. # and copy my_print_defaults to /usr/bin
  32. # - Add the path to the mysql-installation-directory to the basedir variable
  33. # below.
  34. #
  35. # If you want to affect other MySQL variables, you should make your changes
  36. # in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files.
  37. # If you change base dir, you must also change datadir. These may get
  38. # overwritten by settings in the MySQL configuration files.
  39. basedir=
  40. datadir=
  41. # Default value, in seconds, afterwhich the script should timeout waiting
  42. # for server start.
  43. # Value here is overriden by value in my.cnf.
  44. # 0 means don't wait at all
  45. # Negative numbers mean to wait indefinitely
  46. service_startup_timeout=900
  47. # Lock directory for RedHat / SuSE.
  48. lockdir='/var/lock/subsys'
  49. lock_file_path="$lockdir/mysql"
  50. # The following variables are only set for letting mysql.server find things.
  51. # Set some defaults
  52. mysqld_pid_file_path=
  53. if test -z "$basedir"
  54. then
  55. basedir=/usr/local/mysql
  56. bindir=/usr/local/mysql/bin
  57. if test -z "$datadir"
  58. then
  59. datadir=/usr/local/mysql/data
  60. fi
  61. sbindir=/usr/local/mysql/bin
  62. libexecdir=/usr/local/mysql/bin
  63. else
  64. bindir="$basedir/bin"
  65. if test -z "$datadir"
  66. then
  67. datadir="$basedir/data"
  68. fi
  69. sbindir="$basedir/sbin"
  70. libexecdir="$basedir/libexec"
  71. fi
  72. # datadir_set is used to determine if datadir was set (and so should be
  73. # *not* set inside of the --basedir= handler.)
  74. datadir_set=
  75. #
  76. # Use LSB init script functions for printing messages, if possible
  77. #
  78. lsb_functions="/lib/lsb/init-functions"
  79. if test -f $lsb_functions ; then
  80. . $lsb_functions
  81. else
  82. log_success_msg()
  83. {
  84. echo " SUCCESS! $@"
  85. }
  86. log_failure_msg()
  87. {
  88. echo " ERROR! $@"
  89. }
  90. fi
  91. PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin"
  92. export PATH
  93. mode=$1 # start or stop
  94. [ $# -ge 1 ] && shift
  95. other_args="$*" # uncommon, but needed when called from an RPM upgrade action
  96.    # Expected: "--skip-networking --skip-grant-tables"
  97.    # They are not checked here, intentionally, as it is the resposibility
  98.    # of the "spec" file author to give correct arguments only.
  99. case `echo "testing\c"`,`echo -n testing` in
  100. *c*,-n*) echo_n= echo_c=  ;;
  101. *c*,*) echo_n=-n echo_c=  ;;
  102. *)  echo_n= echo_c='\c' ;;
  103. esac
  104. parse_server_arguments() {
  105. for arg do
  106. case "$arg" in
  107.   --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'`
  108.      bindir="$basedir/bin"
  109.    if test -z "$datadir_set"; then
  110.     datadir="$basedir/data"
  111.    fi
  112.    sbindir="$basedir/sbin"
  113.    libexecdir="$basedir/libexec"
  114.   ;;
  115.   --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'`
  116.    datadir_set=1
  117. ;;
  118.   --pid-file=*) mysqld_pid_file_path=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
  119.   --service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
  120. esac
  121. done
  122. }
  123. wait_for_pid () {
  124. verb="$1"   # created | removed
  125. pid="$2"   # process ID of the program operating on the pid-file
  126. pid_file_path="$3" # path to the PID file.
  127. i=0
  128. avoid_race_condition="by checking again"
  129. while test $i -ne $service_startup_timeout ; do
  130. case "$verb" in
  131.   'created')
  132.   # wait for a PID-file to pop into existence.
  133.   test -s "$pid_file_path" && i='' && break
  134.   ;;
  135.   'removed')
  136.   # wait for this PID-file to disappear
  137.   test ! -s "$pid_file_path" && i='' && break
  138.   ;;
  139.   *)
  140.   echo "wait_for_pid () usage: wait_for_pid created|removed pid pid_file_path"
  141.   exit 1
  142.   ;;
  143. esac
  144. # if server isn't running, then pid-file will never be updated
  145. if test -n "$pid"; then
  146.   if kill -0 "$pid" 2>/dev/null; then
  147.   : # the server still runs
  148.   else
  149.   # The server may have exited between the last pid-file check and now.
  150.   if test -n "$avoid_race_condition"; then
  151.    avoid_race_condition=""
  152.    continue # Check again.
  153.   fi
  154.   # there's nothing that will affect the file.
  155.   log_failure_msg "The server quit without updating PID file ($pid_file_path)."
  156.   return 1 # not waiting any more.
  157.   fi
  158. fi
  159. echo $echo_n ".$echo_c"
  160. i=`expr $i + 1`
  161. sleep 1
  162. done
  163. if test -z "$i" ; then
  164. log_success_msg
  165. return 0
  166. else
  167. log_failure_msg
  168. return 1
  169. fi
  170. }
  171. # Get arguments from the my.cnf file,
  172. # the only group, which is read from now on is [mysqld]
  173. if test -x ./bin/my_print_defaults
  174. then
  175. print_defaults="./bin/my_print_defaults"
  176. elif test -x $bindir/my_print_defaults
  177. then
  178. print_defaults="$bindir/my_print_defaults"
  179. elif test -x $bindir/mysql_print_defaults
  180. then
  181. print_defaults="$bindir/mysql_print_defaults"
  182. else
  183. # Try to find basedir in /etc/my.cnf
  184. conf=/etc/my.cnf
  185. print_defaults=
  186. if test -r $conf
  187. then
  188. subpat='^[^=]*basedir[^=]*=\(.*\)$'
  189. dirs=`sed -e "/$subpat/!d" -e 's//\1/' $conf`
  190. for d in $dirs
  191. do
  192.   d=`echo $d | sed -e 's/[  ]//g'`
  193.   if test -x "$d/bin/my_print_defaults"
  194.   then
  195.   print_defaults="$d/bin/my_print_defaults"
  196.   break
  197.   fi
  198.   if test -x "$d/bin/mysql_print_defaults"
  199.   then
  200.   print_defaults="$d/bin/mysql_print_defaults"
  201.   break
  202.   fi
  203. done
  204. fi
  205. # Hope it's in the PATH ... but I doubt it
  206. test -z "$print_defaults" && print_defaults="my_print_defaults"
  207. fi
  208. #
  209. # Read defaults file from 'basedir'. If there is no defaults file there
  210. # check if it's in the old (depricated) place (datadir) and read it from there
  211. #
  212. extra_args=""
  213. if test -r "$basedir/my.cnf"
  214. then
  215. extra_args="-e $basedir/my.cnf"
  216. else
  217. if test -r "$datadir/my.cnf"
  218. then
  219. extra_args="-e $datadir/my.cnf"
  220. fi
  221. fi
  222. parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server`
  223. #
  224. # Set pid file if not given
  225. #
  226. if test -z "$mysqld_pid_file_path"
  227. then
  228. mysqld_pid_file_path=$datadir/`hostname`.pid
  229. else
  230. case "$mysqld_pid_file_path" in
  231. /* ) ;;
  232. * ) mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;;
  233. esac
  234. fi
  235. case "$mode" in
  236. 'start')
  237. # Start daemon
  238. # Safeguard (relative paths, core dumps..)
  239. cd $basedir
  240. echo $echo_n "Starting MySQL"
  241. if test -x $bindir/mysqld_safe
  242. then
  243.   # Give extra arguments to mysqld with the my.cnf file. This script
  244.   # may be overwritten at next upgrade.
  245.   $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
  246.   wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?
  247.   # Make lock for RedHat / SuSE
  248.   if test -w "$lockdir"
  249.   then
  250.   touch "$lock_file_path"
  251.   fi
  252.   exit $return_value
  253. else
  254.   log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)"
  255. fi
  256. ;;
  257. 'stop')
  258. # Stop daemon. We use a signal here to avoid having to know the
  259. # root password.
  260. if test -s "$mysqld_pid_file_path"
  261. then
  262.   mysqld_pid=`cat "$mysqld_pid_file_path"`
  263.   if (kill -0 $mysqld_pid 2>/dev/null)
  264.   then
  265.   echo $echo_n "Shutting down MySQL"
  266.   kill $mysqld_pid
  267.   # mysqld should remove the pid file when it exits, so wait for it.
  268.   wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$?
  269.   else
  270.   log_failure_msg "MySQL server process #$mysqld_pid is not running!"
  271.   rm "$mysqld_pid_file_path"
  272.   fi
  273.   # Delete lock for RedHat / SuSE
  274.   if test -f "$lock_file_path"
  275.   then
  276.   rm -f "$lock_file_path"
  277.   fi
  278.   exit $return_value
  279. else
  280.   log_failure_msg "MySQL server PID file could not be found!"
  281. fi
  282. ;;
  283. 'restart')
  284. # Stop the service and regardless of whether it was
  285. # running or not, start it again.
  286. if $0 stop $other_args; then
  287.   $0 start $other_args
  288. else
  289.   log_failure_msg "Failed to stop running server, so refusing to try to start."
  290.   exit 1
  291. fi
  292. ;;
  293. 'reload'|'force-reload')
  294. if test -s "$mysqld_pid_file_path" ; then
  295.   read mysqld_pid < "$mysqld_pid_file_path"
  296.   kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL"
  297.   touch "$mysqld_pid_file_path"
  298. else
  299.   log_failure_msg "MySQL PID file could not be found!"
  300.   exit 1
  301. fi
  302. ;;
  303. 'status')
  304. # First, check to see if pid file exists
  305. if test -s "$mysqld_pid_file_path" ; then
  306.   read mysqld_pid < "$mysqld_pid_file_path"
  307.   if kill -0 $mysqld_pid 2>/dev/null ; then
  308.   log_success_msg "MySQL running ($mysqld_pid)"
  309.   exit 0
  310.   else
  311.   log_failure_msg "MySQL is not running, but PID file exists"
  312.   exit 1
  313.   fi
  314. else
  315.   # Try to find appropriate mysqld process
  316.   mysqld_pid=`pidof $libexecdir/mysqld`
  317.   # test if multiple pids exist
  318.   pid_count=`echo $mysqld_pid | wc -w`
  319.   if test $pid_count -gt 1 ; then
  320.   log_failure_msg "Multiple MySQL running but PID file could not be found ($mysqld_pid)"
  321.   exit 5
  322.   elif test -z $mysqld_pid ; then
  323.   if test -f "$lock_file_path" ; then
  324.    log_failure_msg "MySQL is not running, but lock file ($lock_file_path) exists"
  325.    exit 2
  326.   fi
  327.   log_failure_msg "MySQL is not running"
  328.   exit 3
  329.   else
  330.   log_failure_msg "MySQL is running but PID file could not be found"
  331.   exit 4
  332.   fi
  333. fi
  334. ;;
  335. *)
  336.   # usage
  337.   basename=`basename "$0"`
  338.   echo "Usage: $basename {start|stop|restart|reload|force-reload|status} [ MySQL server options ]"
  339.   exit 1
  340. ;;
  341. esac
  342. exit 0
复制代码
起首,定义相关参数
  1. basedir=
  2. datadir=
  3. # Default value, in seconds, afterwhich the script should timeout waiting
  4. # for server start.
  5. # Value here is overriden by value in my.cnf.
  6. # 0 means don't wait at all
  7. # Negative numbers mean to wait indefinitely
  8. service_startup_timeout=900
  9. # Lock directory for RedHat / SuSE.
  10. lockdir='/var/lock/subsys'
  11. lock_file_path="$lockdir/mysql"
复制代码
此中,
basedir 指的二进制压缩包解压后地点的目次,譬如/usr/local/mysql。
datadir 指的是数据目次
service_startup_timeout=900 定义mysql服务启动的时间限制,假如在900s中没有启动乐成,则该脚本会退出。
  1. lockdir='/var/lock/subsys'
复制代码
关于/var/lock/subsys,网上的表明如下,后续会用到。
总的来说,系统关闭的过程(发出关闭信号,调用服务自身的进程)中会检查/var/lock/subsys下的文件,逐一关闭每个服务,假如某一运行的服务在/var/lock/subsys下没有相应的选项。在系统关闭的时间,会像杀死普通进程一样杀死这个服务。
通过察看/etc/rc.d/init.d下的脚本,可以发现每个服务自己利用时都会去查察/var/lock/subsys下相应的服务。
很多程序需要判断是否当前已经有一个实例在运行,这个目次就是让程序判断是否有实例运行的标记,好比说xinetd,假如存在这个文件,表示已经有xinetd在运行了,否则就是没有,当然程序内里还要有相应的判断措施来真正确定是否有实例在运行。通常与该目次配套的另有/var/run目次,用来存放对应实例的PID,假如你写脚本的话,会发现这2个目次联合起来可以很方便的判断出很多服务是否在运行,运行的相关信息等等。

判断basedir和datadir
  1. # Set some defaults
  2. mysqld_pid_file_path=
  3. if test -z "$basedir"
  4. then
  5. basedir=/usr/local/mysql
  6. bindir=/usr/local/mysql/bin
  7. if test -z "$datadir"
  8. then
  9. datadir=/usr/local/mysql/data
  10. fi
  11. sbindir=/usr/local/mysql/bin
  12. libexecdir=/usr/local/mysql/bin
  13. else
  14. bindir="$basedir/bin"
  15. if test -z "$datadir"
  16. then
  17. datadir="$basedir/data"
  18. fi
  19. sbindir="$basedir/sbin"
  20. libexecdir="$basedir/libexec"
  21. fi
复制代码
此中,
mysqld_pid_file_path 指定pid文件的路径
-z string 判断字符串是否为空
假如basedir没有表现设置,则默认为/usr/local/mysql,这也是为什么很多mysql安装教程都推荐将mysql相关文件放到/usr/local/mysql下。
假如datadir没有表现设置,则默认为$basedir/data。
定义log_success_msg()和log_failure_msg()函数
起首,判断/lib/lsb/init-functions文件是否存在,假如存在,则使定义在init-functions文件中的所有shell函数在当前脚本中见效。
假如没有,则定义两个函数,一个用于打印乐成日记,一个是打印错误日记。
在RHCS 6.7中,该文件并不存在,已被/etc/init.d/functions所替换。
  1. #
  2. # Use LSB init script functions for printing messages, if possible
  3. #
  4. lsb_functions="/lib/lsb/init-functions"
  5. if test -f $lsb_functions ; then
  6. . $lsb_functions
  7. else
  8. log_success_msg()
  9. {
  10. echo " SUCCESS! $@"
  11. }
  12. log_failure_msg()
  13. {
  14. echo " ERROR! $@"
  15. }
  16. fi
复制代码
转达参数
将第一个参数转达给mode,剩下的参数转达给other_args
  1. PATH="/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin"
  2. export PATH
  3. mode=$1 # start or stop
  4. [ $# -ge 1 ] && shift
  5. other_args="$*" # uncommon, but needed when called from an RPM upgrade action
  6.    # Expected: "--skip-networking --skip-grant-tables"
  7.    # They are not checked here, intentionally, as it is the resposibility
  8.    # of the "spec" file author to give correct arguments only.
  9. case `echo "testing\c"`,`echo -n testing` in
  10. *c*,-n*) echo_n= echo_c=  ;;
  11. *c*,*) echo_n=-n echo_c=  ;;
  12. *)  echo_n= echo_c='\c' ;;
  13. esac
复制代码
剖析配置文件中的参数
这个函数在脚本背面会涉及到。
主要涉及如下参数:--basedir,--datadir,--pid-file,--service-startup-timeout。
  1. parse_server_arguments() {
  2. for arg do
  3. case "$arg" in
  4.   --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'`
  5.      bindir="$basedir/bin"
  6.    if test -z "$datadir_set"; then
  7.     datadir="$basedir/data"
  8.    fi
  9.    sbindir="$basedir/sbin"
  10.    libexecdir="$basedir/libexec"
  11.   ;;
  12.   --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'`
  13.    datadir_set=1
  14. ;;
  15.   --pid-file=*) mysqld_pid_file_path=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
  16.   --service-startup-timeout=*) service_startup_timeout=`echo "$arg" | sed -e 's/^[^=]*=//'` ;;
  17. esac
  18. done
  19. }
复制代码
判断my_print_defaults的位置
起首,它判断当前路径下的bin目次中是否存在该可执行文件,假如不存在,则再判断$bindir(通常指的是$basedir/bin)目次下是否存在。
假如还是没有,则会判断/etc/my.cnf是否存在而且可读,假如是,则判断该配置文件中是否指定了basedir参数,
假如指定了,则取出该参数的值,并判断该值对应的目次中是否存在bin/my_print_defaults可执行文件
末了一步,假如在上述目次中实在没发现my_print_defaults文件,
索性就将print_defaults设置为"my_print_defaults",寄盼望于该下令在当前的PATH情况中。
  1. # Get arguments from the my.cnf file,
  2. # the only group, which is read from now on is [mysqld]
  3. if test -x ./bin/my_print_defaults
  4. then
  5. print_defaults="./bin/my_print_defaults"
  6. elif test -x $bindir/my_print_defaults
  7. then
  8. print_defaults="$bindir/my_print_defaults"
  9. elif test -x $bindir/mysql_print_defaults
  10. then
  11. print_defaults="$bindir/mysql_print_defaults"
  12. else
  13. # Try to find basedir in /etc/my.cnf
  14. conf=/etc/my.cnf
  15. print_defaults=
  16. if test -r $conf
  17. then
  18. subpat='^[^=]*basedir[^=]*=\(.*\)$'
  19. dirs=`sed -e "/$subpat/!d" -e 's//\1/' $conf`
  20. for d in $dirs
  21. do
  22.   d=`echo $d | sed -e 's/[  ]//g'`
  23.   if test -x "$d/bin/my_print_defaults"
  24.   then
  25.   print_defaults="$d/bin/my_print_defaults"
  26.   break
  27.   fi
  28.   if test -x "$d/bin/mysql_print_defaults"
  29.   then
  30.   print_defaults="$d/bin/mysql_print_defaults"
  31.   break
  32.   fi
  33. done
  34. fi
  35. # Hope it's in the PATH ... but I doubt it
  36. test -z "$print_defaults" && print_defaults="my_print_defaults"
  37. fi
复制代码
查找默认的配置文件
-r file 假如文件可读,则为真
  1. #
  2. # Read defaults file from 'basedir'. If there is no defaults file there
  3. # check if it's in the old (depricated) place (datadir) and read it from there
  4. #
  5. extra_args=""
  6. if test -r "$basedir/my.cnf"
  7. then
  8. extra_args="-e $basedir/my.cnf"
  9. else
  10. if test -r "$datadir/my.cnf"
  11. then
  12. extra_args="-e $datadir/my.cnf"
  13. fi
  14. fi
复制代码
剖析配置文件中的参数
my_print_defaults的用法如下:
my_print_defaults --defaults-file=example.cnf client mysql
即读取配置文件中,client和mysql部分的参数配置,
具体在本脚本中,是读取mysqld,server,mysql_server,mysql.server四个部分的配置参数。
  1. parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server`
复制代码
设置pid file的路径
-z string 判断字符串是否为空
假如--pid-file没有在读取到的配置文件中设置或者脚本刚开始的mysqld_pid_file_path参数没有设置,
则pid file默认设置在datadir下,以主机名.pid定名。
假如该参数设置了,还需要进一步判断
假如该参数中带有斜杠,则代表给定的值带有路径,可直接使用。
假如该参数中没带路径,则代表给定的值只是pid的文件名,可将其设在datadir下。
  1. #
  2. # Set pid file if not given
  3. #
  4. if test -z "$mysqld_pid_file_path"
  5. then
  6. mysqld_pid_file_path=$datadir/`hostname`.pid
  7. else
  8. case "$mysqld_pid_file_path" in
  9. /* ) ;;
  10. * ) mysqld_pid_file_path="$datadir/$mysqld_pid_file_path" ;;
  11. esac
  12. fi
复制代码
服务脚本start选项
起首,切换到$basedir中
其次,判断$basedir/bin中的mysqld_safe是否是可执行文件,假如是,则启动mysqld实例,假如不是,则报错退出。
那么,启动流程又是如何实现的呢?
起首,执行$bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &下令,启动mysqld实例。
留意到没有,mysqld_safe实在是在basedir中执行的,包括mysql初始化脚本mysql_install_db,也发起在basedir中执行,具体可参考:
分析MariaDB初始化脚本mysql_install_db
然后通过wait_for_pid函数进行判断,具体可见下文对于wait_for_pid函数的分析
判断完毕后,
查察$lockdir目次是否可写,可写的话,则在目次上创建一个文件。
  1. case "$mode" in
  2. 'start')
  3. # Start daemon
  4. # Safeguard (relative paths, core dumps..)
  5. cd $basedir
  6. echo $echo_n "Starting MySQL"
  7. if test -x $bindir/mysqld_safe
  8. then
  9.   # Give extra arguments to mysqld with the my.cnf file. This script
  10.   # may be overwritten at next upgrade.
  11.   $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
  12.   wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?
  13.   # Make lock for RedHat / SuSE
  14.   if test -w "$lockdir"
  15.   then
  16.   touch "$lock_file_path"
  17.   fi
  18.   exit $return_value
  19. else
  20.   log_failure_msg "Couldn't find MySQL server ($bindir/mysqld_safe)"
  21. fi
  22. ;;
复制代码
wait_for_pid函数
在利用mysqld_safe启动mysql实例后,会调用该参数
  1. wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$?
复制代码
此中$!在shell中用于获取末了运行的后台Process的PID,具体在本例中,是mysqld_safe进程的pid。
由于第一个参数是created,以是会执行
  1. test -s "$pid_file_path" && i='' && break
复制代码
下令。
-s file 假如文件的长度不为零,则为真
该下令的意思是假如pid文件存在,则将变量i设置为空,并退出while循环。
然后执行如下判断,
  1. if test -z "$i" ; then
  2. log_success_msg
  3. return 0
  4. else
  5. log_failure_msg
  6. return 1
  7. fi
复制代码
假如$i为空,则打印乐成日记,并退出脚本,很显然,在pid文件存在的情况下,会将变量i设置为空。
再来看看pid文件不存在的情况
起首,会判断$pid是否不为空(即if test -n "$pid")
假如不为空,则代表在执行完mysqld_safe后,已经捕捉到了该进程的pid。
在这种情况下,进一步通过kill -0 "$pid"确认该进程是否存在。
kill -0就是不发送任何信号,但是系统会进行错误检查,以是经常用来检查一个进程是否存在,当进程不存在时, kill -0 pid会返回错误
假如该进程存在,则不执行任何利用,直接跳到如下利用
  1. echo $echo_n ".$echo_c"
  2. i=`expr $i + 1`
  3. sleep 1
复制代码
将变量i加1,并sleep 1s。
然后,继续while循环,之以是如许做,是思量到mysqld_safe已经执行,但是mysqld实例还在启动过程中,还没创建好pid文件。
不停到$1到达$service_startup_timeout定义的时长。
假如在while循环的过程中,通过kill -0 "$pid"判断到进程已经不存在了,
则会再判断一次,假如这次判断的结果仍旧是pid file不存在,且进程不存在,则会执行
  1. log_failure_msg "The server quit without updating PID file ($pid_file_path)."
复制代码
这就是台甫鼎鼎的“The server quit without updating PID file”的由来。
  1. 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
  2. log_success_msg
  3. return 0
  4. else
  5. log_failure_msg
  6. return 1
  7. fi}
复制代码
服务脚本stop选项
起首,判断pid文件的长度是否不为零。
-s file 假如文件的长度不为零,则为真
此时,会通过pid文件获取mysqld进程的pid,留意,不是mysqld_safe进程的pid
然后,判断mysqld进程是否在正常运行,
假如是,则通过kill $mysqld_pid的方式来关闭mysqld进程
杀死进程最安全的方法是单纯使用kill下令,不加修饰符,不带标记。

标准的kill下令通常会制止有题目的进程,并把进程的资源开释给系统。然而,假如进程启动了子进程,只杀死父进程,子进程仍在运行,因此仍消耗资源。为了防止这些所谓的“僵尸进程”,应确保在杀死父进程之前,先杀死其所有的子进程。

然后,调用wait_for_pid函数进行判断,实在,wait_for_pid函数中设置avoid_race_condition变量的目标是为了stop选项,确实有大概出现,mysqld是在检查pid file之后,检查进程是否存活之前退出的。
假如mysqld进程没有正常运行,在会打印“MySQL server process #$mysqld_pid is not running!”信息,并删除pid文件。
假如在执行stop的时间,判断pid文件的长度为0,则会打印"MySQL server PID file could not be found!"信息。
以是,在pid文件不存在的情况下,通过服务脚本执行stop选项并不会关闭mysqld进程,这个时间,就可通过kill $mysqld_pid的方式来关闭mysqld进程。
  1. 'stop')
  2. # Stop daemon. We use a signal here to avoid having to know the
  3. # root password.
  4. if test -s "$mysqld_pid_file_path"
  5. then
  6.   mysqld_pid=`cat "$mysqld_pid_file_path"`
  7.   if (kill -0 $mysqld_pid 2>/dev/null)
  8.   then
  9.   echo $echo_n "Shutting down MySQL"
  10.   kill $mysqld_pid
  11.   # mysqld should remove the pid file when it exits, so wait for it.
  12.   wait_for_pid removed "$mysqld_pid" "$mysqld_pid_file_path"; return_value=$?
  13.   else
  14.   log_failure_msg "MySQL server process #$mysqld_pid is not running!"
  15.   rm "$mysqld_pid_file_path"
  16.   fi
  17.   # Delete lock for RedHat / SuSE
  18.   if test -f "$lock_file_path"
  19.   then
  20.   rm -f "$lock_file_path"
  21.   fi
  22.   exit $return_value
  23. else
  24.   log_failure_msg "MySQL server PID file could not be found!"
  25. fi
  26. ;;
复制代码
服务脚本restart选项
起首,先执行stop利用,假如stop利用乐成的话,则继续执行start利用。
假如stop利用失败的话,则会输出"Failed to stop running server, so refusing to try to start."信息,并退出脚本。
  1. 'restart')
  2. # Stop the service and regardless of whether it was
  3. # running or not, start it again.
  4. if $0 stop $other_args; then
  5.   $0 start $other_args
  6. else
  7.   log_failure_msg "Failed to stop running server, so refusing to try to start."
  8.   exit 1
  9. fi
  10. ;;
复制代码
服务脚本reload选项
起首,判断pid文件的长度是否为0,假如不为0,则将该文件中的值设置为mysqld_pid变量的值。
然后对该进程执行kill -HUP利用。
  1. kill -HUP pid
复制代码
pid 是进程标识。假如想要更改配置而不需制止并重新启动服务,请使用该下令。在对配置文件作须要的更改后,发出该下令以动态更新服务配置。
根据约定,当您发送一个挂起信号(信号 1 或 HUP)时,大多数服务器进程(所有常用的进程)都会进行复位利用并重新加载它们的配置文件。

假如pid文件的长度为0,则输出"MySQL PID file could not be found!"。
  1. 'reload'|'force-reload')
  2. if test -s "$mysqld_pid_file_path" ; then
  3.   read mysqld_pid < "$mysqld_pid_file_path"
  4.   kill -HUP $mysqld_pid && log_success_msg "Reloading service MySQL"
  5.   touch "$mysqld_pid_file_path"
  6. else
  7.   log_failure_msg "MySQL PID file could not be found!"
  8.   exit 1
  9. fi
  10. ;;
复制代码
服务脚本status选项
起首,判断pid文件长度是否为0,假如不是,则读取该文件中的值,并判断pid对应的进程是否运行正常,
假如运行正常,则输出"MySQL running"
假如不正常,则输出"MySQL is not running, but PID file exists"
假如pid文件的长度为0,则试图通过mysqld的启动下令来获取其pid,
这个时间,大概存在一个mysqld程序启动了多个实例,这会导致pid_count=`echo $mysqld_pid | wc -w`大于1。
这个时间,会输出"Multiple MySQL running but PID file could not be found"信息,并退出脚本。
假如mysqld_pid为空,则会继续判断"$lock_file_path"是否存在,假如存在,
则会输出"MySQL is not running, but lock file ($lock_file_path) exists"信息。
假如"$lock_file_path"不存在,则会输出"MySQL is not running"信息。
假如mysqld_pid即是1,则会输出"MySQL is running but PID file could not be found"信息。
  1. 'status')
  2. # First, check to see if pid file exists
  3. if test -s "$mysqld_pid_file_path" ; then
  4.   read mysqld_pid < "$mysqld_pid_file_path"
  5.   if kill -0 $mysqld_pid 2>/dev/null ; then
  6.   log_success_msg "MySQL running ($mysqld_pid)"
  7.   exit 0
  8.   else
  9.   log_failure_msg "MySQL is not running, but PID file exists"
  10.   exit 1
  11.   fi
  12. else
  13.   # Try to find appropriate mysqld process
  14.   mysqld_pid=`pidof $libexecdir/mysqld`
  15.   # test if multiple pids exist
  16.   pid_count=`echo $mysqld_pid | wc -w`
  17.   if test $pid_count -gt 1 ; then
  18.   log_failure_msg "Multiple MySQL running but PID file could not be found ($mysqld_pid)"
  19.   exit 5
  20.   elif test -z $mysqld_pid ; then
  21.   if test -f "$lock_file_path" ; then
  22.    log_failure_msg "MySQL is not running, but lock file ($lock_file_path) exists"
  23.    exit 2
  24.   fi
  25.   log_failure_msg "MySQL is not running"
  26.   exit 3
  27.   else
  28.   log_failure_msg "MySQL is running but PID file could not be found"
  29.   exit 4
  30.   fi
  31. fi
  32. ;;
复制代码
服务脚本别的选项
假如脚本的第一个参数不是上述几个选项,则会输出Usage信息。
  1. *)
  2.   # usage
  3.   basename=`basename "$0"`
  4.   echo "Usage: $basename {start|stop|restart|reload|force-reload|status} [ MySQL server options ]"
  5.   exit 1
  6. ;;
复制代码
至此,mysql的服务脚天职析完毕~
总结
在通过服务脚本启动mysql的过程中,报“The server quit without updating PID file”错误,有两个条件
起首,pid文件不存在
其次,通过kill -0 $pid检查到进程并不存在
这个时间,只能通过mysql数据库的错误日记来定位。
服务脚本假如不做任何调整的话,默认的basedir是/usr/local/mysql,datadir是/usr/local/mysql/data
假如自己的mysql服务均不是默认路径,
则需要在该脚本中显式设置
经测试,需设置如下几处:
1. 设置basedir和添加conf变量
此中,conf指的是mysqld的配置文件,发起配置文件中显式指定basedir和datadir的值。
在这里,datadir可不设置,由于datadir可通过配置文件来获取。
但是basedir必须要指定,由于要起首根据basedir来判断my_print_deefauts下令
  1. basedir=/usr/local/mysql-advanced-5.6.23-linux-glibc2.5-x86_64
  2. datadir=
  3. conf=/usr/local/mysql-advanced-5.6.23-linux-glibc2.5-x86_64/my_3308.cnf
复制代码
2. 第256行,添加extra_args=" -c $conf"
  1. extra_args=" -e $basedir/my.cnf.bak"
  2. if test -r "$basedir/my.cnf"
  3. then
  4. extra_args="-e $basedir/my.cnf"
  5. else
  6. if test -r "$datadir/my.cnf"
  7. then
  8. extra_args="-e $datadir/my.cnf"
  9. fi
  10. fi
  11. extra_args=" -c $conf"
复制代码
3. 修改285行mysqld_safe的启动参数
  1. $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
复制代码
修改为,
  1.   $bindir/mysqld_safe --defaults-file="$conf" --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
复制代码
主要是添加了--defaults-file选项
以上所述是小编给大家先容的深度剖析MySQL启动时报“The server quit without updating PID file”错误的原因,盼望对大家有所帮助,假如大家有任何疑问请给我留言,小编会实时复兴大家的。在此也非常感谢大家对脚本之家网站的支持!

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作