先了解一下MySQL的shutdown流程
1、启动关机过程。
2、如有必要,服务器创建一个关闭线程。
3、服务器将停止接受新连接。
4、服务器终止当前的活动。
5、服务器关机或关闭存储引擎。
6、在服务器退出。
以上只是官方文档中介绍的一些基本的关机流程,正确的关机命令当然是mysqladmin -xx shutdown。接下来,我们来关注一下我们的问题。
mysqladmin shutdown不但没有关闭掉,反而会restart
提示信息如下:
1 2 |
150105 14:50:47 mysqld_safe Number of processes running now: 0 150105 14:50:47 mysqld_safe mysqld restarted |
奇了个怪了,我的参数明明是shutdown ,为什么提示信息是restart呢? 错误日志中也无明显错误,程序Bug了?
唯一能关闭的方法就是:kill,这是非法的,我们当然不能这样做。
于是尝试了N种方法
* 可能是/usr/local/mysql/data/xxxx.pid文件没有写的权限
* 可能进程里已经存在mysql进程
* 可能是第二次在机器上安装mysql,有残余数据影响了服务的启动。
* mysql在启动时没有指定配置文件时会使用/etc/my.cnf配置文件,请打开这个文件查看在[mysqld]节下有没有指定数据目录(datadir)。
* skip-federated字段问题
* 错误日志目录不存在
* selinux惹的祸,如果是centos系统,默认会开启selinux
很不幸的是,都不是上述问题造成。既然google也找不到,那只能看源码了。
大家都知道,mysqld_safe是启动mysql的守护进程,我想89不离10,应该是由它重启的,那就一窥究竟吧。
源码文件如下: mysqld_safe
下面是部分重要的部分,特拿出来分析,其中添加了一些注释和一些方便调试的断点代码(add by keithlan)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# 后面就是用它启动的mysqld,通过logging变量区分记录日志的类型,分错误日志和系统日志syslog两种 # 最后的eval命令会解析 $cmd 中的值并执行命令 eval_log_error () { cmd="$1" case $logging in file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;; syslog) # mysqld often prefixes its messages with a timestamp, which is # redundant when logging to syslog (which adds its own timestamp) # However, we don't strip the timestamp with sed here, because # sed buffers output (only GNU sed supports a -u (unbuffered) option) # which means that messages may not get sent to syslog until the # mysqld process quits. cmd="$cmd 2>&1 | logger -t '$syslog_tag_mysqld' -p daemon.error" ;; *) echo "Internal program error (non-fatal):" \ " unknown logging method '$logging'" >&2 ;; esac echo "Running mysqld: [$cmd]" --add by Keithlan eval "$cmd" } # 后台循环 执行mysqld log_notice "Starting $MYSQLD daemon with databases from $DATADIR" while true do rm -f $safe_mysql_unix_port "$pid_file" # Some extra safety # 保险起见,又删除了一次pid文件 log_notice "rm -f $safe_mysql_unix_port $pid_file !" #add by Keithlan eval_log_error "$cmd" log_notice " after running mysql " #add by Keithlan last_pid=`ls -l /usr/local/mysql/var/` #add by Keithlan log_notice " last_pid = $last_pid !!" #add by Keithlan # 正常的shutdown 会删除pid文件,如果没有pid文件,会正常退出,如果有,则继续。 # 可想而知,这是唯一跳出循环的地方,这里一定有猫腻。 if test ! -f "$pid_file" # This is removed if normal shutdown then log_notice "$pid_file" #add by Keithlan break fi # mysqld_safe已经启动的处理方法,保证只有一个mysqld_safe程序启动 if true && test $KILL_MYSQLD -eq 1 then # Test if one process was hanging. # This is only a fix for Linux (running as base 3 mysqld processes) # but should work for the rest of the servers. # The only thing is ps x => redhat 5 gives warnings when using ps -x. # kill -9 is used or the process won't react on the kill. # 统计启动的mysqld进程的数目 numofproces=`ps xaww | grep -v "grep" | grep "$ledir/$MYSQLD\>" | grep -c "pid-file=$pid_file"` log_notice "Number of processes running now: $numofproces" I=1 while test "$I" -le "$numofproces" do # 这个PROC的数据即是ps mysqld_safe程序的输出 第一个数字即为进程ID PROC=`ps xaww | grep "$ledir/$MYSQLD\>" | grep -v "grep" | grep "pid-file=$pid_file" | sed -n '$p'` # 使用T来获取进程ID for T in $PROC do break done # echo "TEST $I - $T **" # kill掉该个mysqld_safe程序 if kill -9 $T then log_error "$MYSQLD process hanging, pid $T - killed" else break fi # 每干掉一个mysqld_safe就把I加一,这样没有多余的mysqld_safe时就可以跳出循环了 I=`expr $I + 1` done fi log_notice "mysqld restarted" log_notice "Keithlan" done #mysql shutdown 成功,打印pid文件 log_notice "mysqld from pid file $pid_file ended" |
简单描述一下过程就是:myqsld_safe会用eval去启动mysqld,在后台运行,知道接受到kill命令,或者shutdown进程来kill掉它。
如果是非正常kill,mysqld_safe会一直监控,将mysqld进程restart起来。
经过调试后,手动去rm -f 掉pid文件, 然后再mysqladmin shutdown,是可以正常关闭的,很明显,就能定位到问题就出在pid上。为什么mysqladmin进程shutdown的时候没有删除掉pid文件呢?首先可以排除掉权限等问题,因为既然能够create pid,当然可以delete pid咯。问题一定是mysqladmin哪个地方出问题了。果然,根据线索找到mysqladmin代码中断言出现问题Failing assertion: UT_LIST_GET_LEN(rseg->update_undo_list) == 0,网上搜了一堆的bug。后来,想想,是不是表空间出了问题,mysql shutdown的时候会去回收表空间记录,如果回收不成功,导致不能normal shutdown,导致无法删除掉pid文件。 于是,将表空间重建后,问题消失。
表空间重建实战(独立表空间)
1)创建环境
1 2 3 4 |
mysql> CREATE DATABASE test CHARSET utf8mb4; mysql> USE test; mysql> CREATE TABLE tn1(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,NAME CHAR(8)); mysql> INSERT INTO tn1(NAME) VALUES('aa'),('bb'),('cc'),('dd'); |
2)备份DB
1 |
$ mysqldump -uroot -p --databases test > /tmp/test.sql |
3)db所在的目录情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ ls -all /var/lib/mysql/ total 122960 drwxr-x--x 6 mysql mysql 4096 Dec 21 11:52 . drwxr-xr-x. 28 root root 4096 Dec 21 11:43 .. -rw-r----- 1 mysql mysql 56 Dec 21 11:48 auto.cnf -rw-r----- 1 mysql mysql 413 Dec 21 11:48 ib_buffer_pool -rw-r----- 1 mysql mysql 12582912 Dec 21 11:55 ibdata1 -rw-r----- 1 mysql mysql 50331648 Dec 21 11:55 ib_logfile0 -rw-r----- 1 mysql mysql 50331648 Dec 21 11:48 ib_logfile1 -rw-r----- 1 mysql mysql 12582912 Dec 21 11:48 ibtmp1 drwxr-x--- 2 mysql mysql 8192 Dec 21 11:48 sys drwxr-x--- 2 mysql mysql 47 Dec 21 11:54 test drwxr-x--- 2 mysql mysql 4096 Dec 21 11:48 mysql srwxrwxrwx 1 mysql mysql 0 Dec 21 11:48 mysql.sock -rw------- 1 mysql mysql 6 Dec 21 11:48 mysql.sock.lock drwxr-x--- 2 mysql mysql 8192 Dec 21 11:48 performance_schema |
5)查看所有的db
1 2 3 4 5 6 7 8 9 10 11 |
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | test | +--------------------+ 5 rows in set (0.00 sec) |
6)删除掉占用空间的
1 |
mysql> drop database test; |
7)停止MySQL
1 2 |
$ systemctl stop mysqld.service Shutting down MySQL.. [ok] |
8)删除innodb相关文件
1 2 3 4 |
$ rm -rf /var/lib/mysql/ibdata1 $ rm -rf /var/lib/mysql/ib_logfile0 $ rm -rf /var/lib/mysql/ib_logfile1 $ rm -rf /var/lib/mysql/ibtmp1 |
9)启动MySQL
1 2 |
$ service mysql start Starting MySQL.................... [ok] |
此时文件重新生成了
1 2 3 4 5 6 7 8 9 10 11 12 |
$ ls /var/lib/mysql -rw-r----- 1 mysql mysql 56 Dec 21 11:48 auto.cnf -rw-r----- 1 mysql mysql 387 Dec 21 12:01 ib_buffer_pool -rw-r----- 1 mysql mysql 12582912 Dec 21 12:02 ibdata1 -rw-r----- 1 mysql mysql 50331648 Dec 21 12:02 ib_logfile0 -rw-r----- 1 mysql mysql 50331648 Dec 21 12:02 ib_logfile1 -rw-r----- 1 mysql mysql 12582912 Dec 21 12:02 ibtmp1 drwxr-x--- 2 mysql mysql 4096 Dec 21 11:48 mysql srwxrwxrwx 1 mysql mysql 0 Dec 21 12:02 mysql.sock -rw------- 1 mysql mysql 6 Dec 21 12:02 mysql.sock.lock drwxr-x--- 2 mysql mysql 8192 Dec 21 11:48 performance_schema drwxr-x--- 2 mysql mysql 8192 Dec 21 11:48 sys |
10)进入查询下MySQL是否正常
1 2 3 4 5 6 7 8 9 10 |
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.00 sec) |
11)建库、重新导入
1 2 3 4 5 6 7 |
mysql> CREATE DATABASE test charset utf8mb4; Query OK, 1 row affected (0.01 sec) mysql> USE test; Database changed mysql> source /tmp/test.sql; |
数据都正常
1 2 3 4 5 6 7 8 9 10 |
mysql> select * from tn1; +----+------+ | ID | NAME | +----+------+ | 1 | aa | | 2 | bb | | 3 | cc | | 4 | dd | +----+------+ 4 rows in set (0.00 sec) |
理想很美好,现实就是mysql库中有些表由于表空间文件不存在了,所以有问题。
1 2 |
mysql> flush privileges; ERROR 1146 (42S02): Table 'mysql.servers' doesn't exist |
PS:这个表空间文件重建只是一个理想状态,如果数据导不出来的话,删除表空间文件就会造成表数据无法正常读取的。
原文出处:http://keithlan.github.io/2015/07/14/mysql_shutdown_err/