MySQL:一个奇怪的hang案例
作者:互联网
mysql客户端无法登陆,查看服务器负载没有发现高负载信息。通过pstack查看线程栈信息,没有发现异常信息。
二、问题诊断和解决
=============
一般来讲出现这种情况,我们会使用pstack看看新建立的线程为在什么函数上卡住了,然后很容易就能找到原因。但是出现这个问题过后,当mysql发起连接后卡住后,使用pstack查看mysqld服务端的进程,发现根本就没有线程与之进行交互,因此mysqld怀疑监听线程是不是出了什么问题,因此对mysql客户端连接进程进行了pstack发现如下:
#0 0x00007f262e7889c0 in __connect_nocancel () from /lib64/libpthread.so.0
#1 0x0000000000435123 in inline_mysql_socket_connect (src_file=0x504490 “…/…/mysql-8.0.20/vio/viosocket.cc”, src_line=1054, len=110, addr=0x7fffb38daa20, mysql_socket=…) at …/…/mysql-8.0.20/include/mysql/psi/mysql_socket.h:647
#2 vio_socket_connect (vio=0x110d2c0, addr=addr@entry=0x7fffb38daa20, len=len@entry=110, nonblocking=, timeout=-1, connect_done=connect_done@entry=0x7fff
【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
b38da9ef) at …/…/mysql-8.0.20/vio/viosocket.cc:1054
#3 0x0000000000422ad6 in csm_begin_connect (ctx=0x7fffb38daad0) at …/…/mysql-8.0.20/sql-common/client.cc:250
#4 0x000000000041fe99 in mysql_real_connect (mysql=mysql@entry=0xaf16a0 , host=host@entry=0x0, user=user@entry=0x10defc0 “root”, passwd=passwd@entry=0x10deff0 “xsh0923”, db=db@entry=0x0, port=, unix_socket=0x10e0860 “/data/mysql3306/tmp/mysql.sock”, client_flag=66560) at …/…/mysql-8.0.20/sql-common/client.cc:5600
#5 0x000000000040ddb6 in sql_real_connect (silent=0, password=0x10deff0 “xsh0923”, user=0x10defc0 “root”, database=0x0, host=0x0) at …/…/mysql-8.0.20/client/mysql.cc:4515
#6 sql_connect (host=0x0, database=0x0, user=0x10defc0 “root”, password=0x10deff0 “xsh0923”, silent=) at …/…/mysql-8.0.20/client/mysql.cc:4670
#7 0x000000000040968b in main () at …/…/mysql-8.0.20/client/mysql.cc:1326
#8 0x00007f262ce3e495 in __libc_start_main () from /lib64/libc.so.6
#9 0x000000000040a4e1 in _start () at …/…/mysql-8.0.20/include/my_alloc.h:86
实际上mysql卡住connect函数上面如下:
因此mysql客户端和mysqld服务端都还没有建立好连接,netstat查看如下:
unix 2 [ ACC ] STREAM LISTENING 8584712 29676/mysqld /tmp/mysqlx.sock
unix 7 [ ACC ] STREAM LISTENING 8584713 29676/mysqld /data/mysql3306/tmp/mysql.sock
unix 2 [ ] STREAM CONNECTING 0 - /data/mysql3306/tmp/mysql.sock
unix 2 [ ] STREAM CONNECTING 0 - /data/mysql3306/tmp/mysql.sock
unix 2 [ ] STREAM CONNECTING 0 - /data/mysql3306/tmp/mysql.sock
大量connectint状态的连接
然后做了strace mysqld的监听线程的操作,得到的结果如下:
正常的情况下这里应该是poll和accept然后开启新的(或者从缓存线程中拿一个)线程来处理交互信息了。但是这里我们可以清晰的看到出现了SIGSTOP信号。随即查看正mysqld线程的状态如下:
所有的线程都处于T状态下,这个状态正是由于信息SIGSTOP引起的。因此我们发一个SIGCONT信号给mysqld进程就好了如下:
kill -18 29676
推荐观看:Mysql精讲教程
三、关于信号
==========
下面是我学习信号的时候一些笔记。
在Linux中信号是一种由内核处理的一种软中断机制,他满足简单、不能携带大量信息、并且要满足一定条件才会发送等特征。信号会经历如下过程:
- 产生–>阻塞信号集–>未决信号集–>信号递达–>信号处理方式
首先信号的产生可以有多种方式比如我们经常用的kill命令就是发起信号的一种手段如下:
[root@mgr4 8277]# kill -l
-
SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
-
SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
-
SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
-
SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
-
SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
-
SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
-
SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
-
SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
-
SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
-
SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
-
SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
-
SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
-
SIGRTMAX-1 64) SIGRTMAX
我们经常的按键也可以产生
-
Ctrl+c 2)SIGINT
-
Ctrl+\ 3)SIGQUIT
-
Ctrl+z 4)SIGTSTP
当然还有很多其他触发方式比如硬件异常,raise函数,abort函数,alarm函数等等。其次是阻塞信号集,阻塞信号集能够对想除(9/19号信号以外)的信号进行屏蔽,如果屏蔽后信号自然不会到达 递达 状态,也就谈不上处理了。通过sigprocmask函数进行阻塞信号集的设置,但之前必须要设置sigset_t 集合,通过sigaddset、sigdelset、sigemptyset、sigfillset等函数设置。
未决信号集是不能被操作的,只能被获取通过sigpending函数获取,但是他和阻塞信号集一起可以控制信号的递达。
信号递达后就需要处理信号,默认的行为上面都列举了,但是信号(9/19号信号以外)是可以被捕获改变其处理方式的,我们可以自定义函数作为某个信号的处理方式,这可以通过signal函数和sigaction函数进行捕获和处理,sigaction函数相对复杂需要有一个struct sigaction的结构体变量,其中包含了sa_handler\sa_mask\sa_flags\sa_siaction 成员,这里不做解释可以自行查看Linux man page。
上面是单进程下的信号处理方式,在多线程下,线程之间公用处理方式,但是可以有不同的信号屏蔽集,在多线程下一般采用设置统一的信号屏蔽字和信号处理方式使用pthread_mask函数继承到各个线程,同时使用sigwait/sigwaitinfo等函数设置一个单独的信号处理线程来进行统一处理,MySQL就是这样处理的。MySQL实际上有一个信号处理线程,
| 36 | 1927 | sql/signal_handler | NULL | BACKGROUND | NULL | NULL |
如果需要了解可以参考如下文章: MySQL中对信号的处理(SIGTERM,SIGQUIT,SIGHUP等 ) , http://blog.itpub.net/7728585/viewspace-2142060/
四、pstack和SIGSTOP
====================
这里简单提一下pstack抓取线程栈的时候会触发一个SIGSTOP信号,因为pstack实际上调用的就是gdb的如下命令:
/usr/bin/gdb --quiet -nx /proc/8277/exe 8277
thread apply all bt
输出所有线程信息
gdb通常会发起SIGSTOP信号来停止进程的运行,因此我们在线上执行pstack命令的时候一定要小心,pstack可能导致你的MySQL停止运行一小会,特别是高压力线程很多负载很高的情况下,可能需要很长的时候, pstack应该作为线上重要数据库最后不得已而为之的诊断方式 。
如下我们使用pstack 来获取mysqld的栈,运行一开始就关闭窗口,此时我们的mysqld已经处于停止状态如下:
因此使用pstack一定要格外小心。一般来讲从库线程不多压力不大重要性不高可以使用pstack进行信息采集。当然也可以手动发起SIGSTOP信号来达到测试效果。
五、捕获所有的信号
=============
下面一个小代码可以捕获所有的信号供测试:
标签:SIGRTMAX,MySQL,hang,pstack,案例,线程,信号,mysql,SIGRTMIN 来源: https://blog.csdn.net/m0_63174529/article/details/121182613