MySQL DBA如何利用strace/pstack/gdb来定位问题

数据库 MySQL
本文内容比较多,给大家介绍了几个MySQL DBA(可能其他岗位同样适用)应该掌握的必备工具。

[[328542]]

 strace简介

strace是Linux环境下的一款程序调试工具,用来监察一个应用程序所使用的系统调用。

Strace是一个简单的跟踪系统调用执行的工具。在其最简单的形式中,它可以从开始到结束跟踪二进制的执行,并在进程的生命周期中输出一行具有系统调用名称,每个系统调用的参数和返回值的文本行。

常用选项

  •  -T:strace输出显示时间
  •  -t:可以在每行的输出之前添加时间戳,-tt可以精确到微妙级别,-ttt也可以精确到微妙级,但是它并不是打印当前时间,而是显示自从epoch以来的所经过的秒数
  •  -s:指定输出的字符串的最大长度,默认为32,如果输出到文件的话会全部输出
  •  -o:指定将strace输出到文件
  •  -ff:如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
  •  -e:指定跟踪某个行为,例如-e trace=open指定只跟踪open行为
  •  -y:将文件句柄用文件路径代替显示

正确姿势

简单使用 

  1. strace -T -tt -o /tmp/strace.log CMD  
  2. strace -T -tt CMD 2>&1 |tee /tmp/strace.log  
  3. strace -T -tt -s 100 -o /tmp/strace.log CMD  
  4. strace -T -tt -s 100 -ff -o /tmp/strace.log CMD  
  5. strace -T -tt -s 100 -e trace=XXXX -o /tmp/strace.log CMD 

使用案例

利用strace观察客户端client执行SQL 

  1. #通过sys.processlist表中pid可以知道客户端连接pid  
  2. MySQL [sys]> select thd_id,conn_id,user,pid,program_name,command,current_statement from processlist where conn_id>0 and pid>0;  
  3. +--------+---------+------------------+------+--------------+---------+-------------------+  
  4. | thd_id | conn_id | user             | pid  | program_name | command | current_statement |  
  5. +--------+---------+------------------+------+--------------+---------+-------------------+  
  6. |     78 |      22 | xucl@172.17.0.11 | 8656 | mysql        | Sleep   | select * from t1  |  
  7. +--------+---------+------------------+------+--------------+---------+-------------------+  
  8. 1 row in set (0.05 sec)  
  9. #session1  
  10. [root@VM_0_11_centos ~]# strace -T -tt -s 100 -o /tmp/strace.log -p 5841  
  11. strace: Process 5841 attached  
  12. ^Cstrace: Process 5841 detached  
  13. #session进行数据查询   
  14. MySQL [(none)]> use xucl  
  15. Reading table information for completion of table and column names  
  16. You can turn off this feature to get a quicker startup with -A  
  17. Database changed  
  18. MySQL [xucl]> select * from t1;  
  19. +----+----+  
  20. | id | c1 |  
  21. +----+----+  
  22. |  1 |    |  
  23. +----+----+  
  24. 1 row in set (0.00 sec) 

需要提醒一下的是从客户端连接到MySQL的时候需要注意客户端的版本,例如我用mariadb的客户端连接官方版本8.0就无法获取到pid

查看strace文件 

  1. ....  
  2. 20:54:16.901980 poll([{fd=3events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout) <0.000006>  
  3. 20:54:16.902001 write(3, "\21\0\0\0\3select * from t1", 21) = 21 <0.000011>  
  4. 20:54:16.902026 read(3, "\1\0\0\1\2\"\0\0\2\3def\4xucl\2t1\2t1\2id\2id\f?\0\v\0\0\0\3\3P\0\0\0\"\0\0\3\3def\4xucl\2t1\2t1\2c1\2c1\f!\0\36\0\0\0\375\t@\0\0\0\5\0\0\4\376\0\0\2\0\3\0\0\5\0011\0\5\0\0"..., 16384) = 106 <0.000355> 
  5. 20:54:16.902405 times({tms_utime=0tms_stime=0tms_cutime=0tms_cstime=0}) = 429630696 <0.000005>  
  6. 20:54:16.902441 write(1, "+----+----+\n", 12) = 12 <0.000026>  
  7. 20:54:16.902482 write(1, "| id | c1 |\n", 12) = 12 <0.000018>  
  8. 20:54:16.902514 write(1, "+----+----+\n", 12) = 12 <0.000018>  
  9. 20:54:16.902549 write(1, "|  1 |    |\n", 12) = 12 <0.000019>  
  10. 20:54:16.902582 write(1, "+----+----+\n", 12) = 12 <0.000019>  
  11. 20:54:16.902631 write(1, "\33(B\33[0;1m1 row in set (0.00 sec)\n", 33) = 33 <0.000019>  
  12. 20:54:16.902666 write(1, "\33(B\33[m\33(B\33[0;1m\n", 16) = 16 <0.000019>  
  13. 20:54:16.902702 stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=528, ...}) = 0 <0.000006>  
  14. 20:54:16.902752 rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0 <0.000004>  
  15. 20:54:16.902771 ioctl(0, TIOCGWINSZ, {ws_row=42ws_col=163ws_xpixel=0ws_ypixel=0}) = 0 <0.000005>  
  16. 20:54:16.902789 ioctl(0, TIOCSWINSZ, {ws_row=42ws_col=163ws_xpixel=0ws_ypixel=0}) = 0 <0.000004>  
  17. 20:54:16.902806 ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0 <0.000005>  
  18. 20:54:16.902824 ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig -icanon -echo ...}) = 0 <0.000006>  
  19. 20:54:16.902841 write(1, "\33(B\33[m", 6) = 6 <0.000022>  
  20. 20:54:16.902877 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 <0.000005>  
  21. 20:54:16.902895 rt_sigprocmask(SIG_BLOCK, [INT QUIT ALRM TSTP TTIN TTOU], [], 8) = 0 <0.000005>  
  22. 20:54:16.902912 rt_sigaction(SIGINT, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {0x55e19fee21d0, [INT], SA_RESTORER|SA_RESTART, 0x7fc3e63585f0}, 8) = 0 <0.000005> 
  23. 20:54:16.902931 rt_sigaction(SIGTERM, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {SIG_DFL, [], SA_RESTORER, 0x7fc3e63585f0}, 8) = 0 <0.000005> 
  24. 20:54:16.902948 rt_sigaction(SIGQUIT, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {0x55e19fee1fa0, [QUIT], SA_RESTORER|SA_RESTART, 0x7fc3e63585f0}, 8) = 0 <0.000005> 
  25. 20:54:16.902966 rt_sigaction(SIGALRM, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {SIG_DFL, [], SA_RESTORER, 0x7fc3e63585f0}, 8) = 0 <0.000005> 
  26. 20:54:16.902983 rt_sigaction(SIGTSTP, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {SIG_DFL, [], SA_RESTORER, 0x7fc3e63585f0}, 8) = 0 <0.000005> 
  27. 20:54:16.903001 rt_sigaction(SIGTTOU, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {SIG_DFL, [], SA_RESTORER, 0x7fc3e63585f0}, 8) = 0 <0.000004> 
  28. 20:54:16.903018 rt_sigaction(SIGTTIN, {0x55e19ff48a00, [], SA_RESTORER, 0x7fc3e63585f0}, {SIG_DFL, [], SA_RESTORER, 0x7fc3e63585f0}, 8) = 0 <0.000006> 
  29. 20:54:16.903037 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 <0.000005> 
  30. 20:54:16.903055 rt_sigaction(SIGWINCH, {0x55e19ff481a0, [], SA_RESTORER|SA_RESTART, 0x7fc3e63585f0}, {0x55e19fee0760, [WINCH], SA_RESTORER|SA_RESTART, 0x7fc3e63585f0}, 8) = 0 <0.000006> 
  31. 20:54:16.903088 write(1, "MySQL [xucl]> ", 14) = 14 <0.000032> 
  32. 20:54:16.903139 read(0,  <detached ...> 

利用strace观察server端执行 

  1. #命令  
  2. strace -o /tmp/strace.log -T -tt -f  -p `pidof mysqld`  
  3. #假设你想看跟IO相关的行为,可以用-e trace来指定  
  4. # mysql相关的IO操作:  
  5. # mysql: read, write, open  
  6. # innodb: pread64, pwrite64  
  7. strace -o /tmp/strace.log -T -tt -f -e trace=read,open,write,pwrite64,pread64 -p `pidof mysqld` 

示例: 

  1. strace -o /tmp/strace.log -T -tt -ff  -p `pidof mysqld`  
  2. #然后在本地通过socket登录执行命令  
  3. MySQL [performance_schema]> use xucl;  
  4. Reading table information for completion of table and column names  
  5. You can turn off this feature to get a quicker startup with -A 
  6. Database changed  
  7. MySQL [xucl]> select * from t1;  
  8. +------+  
  9. | id   |  
  10. +------+  
  11. |    1 |  
  12. |    2 |  
  13. |    4 |  
  14. |    3 |  
  15. |    5 |  
  16. |    6 |  
  17. +------+  
  18. 6 rows in set (0.01 sec)  
  19. #查看本线程对应的LWP  
  20. MySQL [xucl]> show processlist;  
  21. +----+------+-----------+------+---------+------+----------+------------------+  
  22. | Id | User | Host      | db   | Command | Time | State    | Info             |  
  23. +----+------+-----------+------+---------+------+----------+------------------+  
  24. | 28 | root | localhost | xucl | Query   |    0 | starting | show processlist |  
  25. +----+------+-----------+------+---------+------+----------+------------------+  
  26. 1 row in set (0.00 sec)  
  27. MySQL [xucl]> select * from performance_schema.threads where processlist_id=28\G  
  28. *************************** 1. row ***************************  
  29.           THREAD_ID: 62  
  30.                NAME: thread/sql/one_connection  
  31.                TYPE: FOREGROUND  
  32.      PROCESSLIST_ID: 28  
  33.    PROCESSLIST_USER: root  
  34.    PROCESSLIST_HOST: localhost  
  35.      PROCESSLIST_DB: xucl  
  36. PROCESSLIST_COMMAND: Query  
  37.    PROCESSLIST_TIME: 0  
  38.   PROCESSLIST_STATE: Sending data  
  39.    PROCESSLIST_INFO: select * from performance_schema.threads where processlist_id=28  
  40.    PARENT_THREAD_ID: NULL  
  41.                ROLE: NULL  
  42.        INSTRUMENTED: YES  
  43.             HISTORY: YES  
  44.     CONNECTION_TYPE: Socket  
  45.        THREAD_OS_ID: 8208  
  46. 1 row in set (0.00 sec) 

strace会在/tmp文件夹下生成strace.log.xxx,xxx对应的就是THREAD_OS_ID,我们直接看strace.log.8208文件 

  1. t@VM_0_9_centos tmp]# cat strace.log.8208  
  2. 16:44:14.447709 restart_syscall(<... resuming interrupted restart_syscall ...>) = 1 <3.268331>  
  3. 16:44:17.716076 recvfrom(134<socket:[12555437]>, "\22\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000015>  
  4. 16:44:17.716179 recvfrom(134<socket:[12555437]>, "\3SELECT DATABASE()", 18, MSG_DONTWAIT, NULL, NULL) = 18 <0.000017>  
  5. 16:44:17.716318 sendto(134<socket:[12555437]>, "\1\0\0\1\1 \0\0\2\3def\0\0\0\nDATABASE()\0\f!\0f"..., 68, MSG_DONTWAIT, NULL, 0) = 68 <0.000033>  
  6. 16:44:17.716420 recvfrom(134<socket:[12555437]>, "\5\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000015>  
  7. 16:44:17.716476 recvfrom(134<socket:[12555437]>, "\2xucl", 5, MSG_DONTWAIT, NULL, NULL) = 5 <0.000017>  
  8. 16:44:17.716538 access("./xucl", F_OK)  = 0 <0.000016>  
  9. 16:44:17.716604 sendto(134<socket:[12555437]>, "\7\0\0\1\0\0\0\2\0\0\0", 11, MSG_DONTWAIT, NULL, 0) = 11 <0.000090>  
  10. 16:44:17.716738 recvfrom(134<socket:[12555437]>, 0x7f06ec009fe0, 4, MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000014>  
  11. 16:44:17.716793 poll([{fd=134<socket:[12555437]>events=POLLIN|POLLPRI}], 1, 600000) = 1 ([{fd=134revents=POLLIN}]) <1.326139>  
  12. 16:44:19.042985 recvfrom(134<socket:[12555437]>, "\21\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000014>  
  13. 16:44:19.043048 recvfrom(134<socket:[12555437]>, "\3select * from t1", 17, MSG_DONTWAIT, NULL, NULL) = 17 <0.000017>  
  14. 16:44:19.043256 sendto(134<socket:[12555437]>, "\1\0\0\1\1\"\0\0\2\3def\4xucl\2t1\2t1\2id\2id\f?"..., 97, MSG_DONTWAIT, NULL, 0) = 97 <0.000137>  
  15. 16:44:19.043448 recvfrom(134<socket:[12555437]>, 0x7f06ec009fe0, 4, MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000014>  
  16. 16:44:19.043501 poll([{fd=134<socket:[12555437]>events=POLLIN|POLLPRI}], 1, 600000) = 1 ([{fd=134revents=POLLIN}]) <11.279983>  
  17. 16:44:30.323556 recvfrom(134<socket:[12555437]>, "\21\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000050>  
  18. 16:44:30.323682 recvfrom(134<socket:[12555437]>, "\3show processlist", 17, MSG_DONTWAIT, NULL, NULL) = 17 <0.000017>  
  19. 16:44:30.323788 sendto(134<socket:[12555437]>, "\1\0\0\1\10\30\0\0\2\3def\0\0\0\2Id\0\f?\0\25\0\0\0\10\201\0\0\0"..., 324, MSG_DONTWAIT, NULL, 0) = 324 <0.000221> 
  20. 16:44:30.324066 recvfrom(134<socket:[12555437]>, 0x7f06ec009fe0, 4, MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000014> 
  21. 16:44:30.324120 poll([{fd=134<socket:[12555437]>events=POLLIN|POLLPRI}], 1, 600000) = 1 ([{fd=134revents=POLLIN}]) <2.943989>  
  22. 16:44:33.268173 recvfrom(134<socket:[12555437]>, "A\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000055>  
  23. 16:44:33.268309 recvfrom(134<socket:[12555437]>, "\3select * from performance_schem"..., 65, MSG_DONTWAIT, NULL, NULL) = 65 <0.000017>  
  24. 16:44:33.268592 sendto(134<socket:[12555437]>, "\1\0\0\1\21H\0\0\2\3def\22performance_schema"..., 1606, MSG_DONTWAIT, NULL, 0) = 1606 <0.000142>  
  25. 16:44:33.268790 recvfrom(134<socket:[12555437]>, 0x7f06ec009fe0, 4, MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000018>  
  26. 16:44:33.268853 poll([{fd=134<socket:[12555437]>events=POLLIN|POLLPRI}], 1, 600000 <detached ...> 

可以清楚地看到有对应的SQL输出

pstack简介

pstack用来跟踪进程栈,这个命令在排查进程问题时非常有用,比如我们发现一个服务一直处于work状态(如假死状态,好似死循环),使用这个命令就能轻松定位问题所在;

可以在一段时间内,多执行几次pstack,若发现代码栈总是停在同一个位置,那个位置就需要重点关注,很可能就是出问题的地方;

正确使用姿势

pstack使用起来非常简单,直接pstack -pid即可 

  1. [root@VM_0_9_centos mysql3306]# netstat -lntp|grep 3306  
  2. tcp6       0      0 :::33060                :::*                    LISTEN      13499/mysqld  
  3. tcp6       0      0 :::3306                 :::*                    LISTEN      13499/mysqld  
  4. [root@VM_0_9_centos mysql3306]# ps -Lf 13499  
  5. UID        PID  PPID   LWP  C NLWP STIME TTY      STAT   TIME CMD  
  6. mysql    13499 11316 13499  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  7. mysql    13499 11316 13506  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  8. mysql    13499 11316 13507  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  9. mysql    13499 11316 13508  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  10. mysql    13499 11316 13509  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  11. mysql    13499 11316 13510  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  12. mysql    13499 11316 13511  0   53 20:31 pts/0    Sl     0:00 /usr/local/mysql/bin/mysqld --defaults-file=/data/mysql/mysql3306/my3306.cnf --user=mysql  
  13. .... 

mysqld的pid为13499,fork了几十个线程,有的是用户连接,有的是MySQL自己的线程,例如read_thread、write_thread等等,这里的LWP对应的就是performance_schema.threads表的THREAD_OS_ID。

全部抓取

假如发生了mysqld hang住的情况,可以直接pstack mysqldpid来抓取所有的线程堆栈,这也是通常使用的方式,例如 

  1. pstack 13499 >> /tmp/pstack.log 

这里简单随便看下抓取到的pstack 

  1. Thread 27 (Thread 0x7ff1bc29f700 (LWP 13535)):  
  2. #0  0x00007ff1f6368da2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  3. #1  0x00000000020ace25 in os_event::timed_wait(timespec const*) () at ../../../mysql-8.0.19/storage/innobase/include/sync0types.h:540  
  4. #2  0x00000000020ad9e1 in os_event::wait_time_low (this=0x7ff1dc022458time_in_usec=<optimized out>reset_sig_count=1) at ../../../mysql-8.0.19/storage/innobase/os/os0event.cc:495 
  5. #3  0x000000000215fe0c in srv_error_monitor_thread() () at ../../../mysql-8.0.19/storage/innobase/srv/srv0srv.cc:1835 
  6. #4  0x0000000002076da5 in __invoke_impl<void, void (*&)()> (__f=<synthetic pointer>) at /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/invoke.h:89  
  7. #5  __invoke<void (*&)()> (__fn=<synthetic pointer>) at /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/invoke.h:95  
  8. #6  __call<void> (__args=..., this=<synthetic pointer>) at /opt/rh/devtoolset-8/root/usr/include/c++/8/functional:400  
  9. #7  operator()<> (this=<synthetic pointer>) at /opt/rh/devtoolset-8/root/usr/include/c++/8/functional:484  
  10. #8  operator()<void (*)()> (f=@0x7ff1dc4aec18: 0x215fd20 <srv_error_monitor_thread()>this=0x7ff1dc4aec20) at ../../../mysql-8.0.19/storage/innobase/include/os0thread-create.h:101 
  11. #9  __invoke_impl<void, Runnable, void (*)()> (__f=...) at /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/invoke.h:60  
  12. #10 __invoke<Runnable, void (*)()> (__fn=...) at /opt/rh/devtoolset-8/root/usr/include/c++/8/bits/invoke.h:95  
  13. #11 _M_invoke<0, 1> (this=0x7ff1dc4aec18) at /opt/rh/devtoolset-8/root/usr/include/c++/8/thread:244  
  14. #12 operator() (this=0x7ff1dc4aec18) at /opt/rh/devtoolset-8/root/usr/include/c++/8/thread:253  
  15. #13 std::thread::_State_impl<std::thread::_Invoker<std::tuple<Runnable, void (*)()> > >::_M_run (this=0x7ff1dc4aec10) at /opt/rh/devtoolset-8/root/usr/include/c++/8/thread:196 
  16. #14 0x000000000265d64f in execute_native_thread_routine ()  
  17. #15 0x00007ff1f6364e65 in start_thread () from /lib64/libpthread.so.0  
  18. #16 0x00007ff1f44a188d in clone () from /lib64/libc.so.6 

从堆栈可以看出这其实就是MySQL的error monitor线程

单个抓取

假设你运气好,你现在可以通过performance_schema.threads可以查到对应的线程的thread_os_id,你可以指定单个线程的lwp进行抓取 

  1. MySQL [performance_schema]> show processlist;  
  2. +----+-----------------+-----------+--------------------+---------+------+------------------------+---------------------------+  
  3. | Id | User            | Host      | db                 | Command | Time | State                  | Info                      |  
  4. +----+-----------------+-----------+--------------------+---------+------+------------------------+---------------------------+  
  5. |  4 | event_scheduler | localhost | NULL               | Daemon  | 4319 | Waiting on empty queue | NULL                      |  
  6. | 23 | root            | localhost | performance_schema | Query   |    0 | starting               | show processlist          |  
  7. | 24 | root            | localhost | xucl               | Query   |   22 | User sleep             | select sleep(600) from t1 |  
  8. +----+-----------------+-----------+--------------------+---------+------+------------------------+---------------------------+  
  9. 3 rows in set (0.00 sec)  
  10. MySQL [performance_schema]> select THREAD_OS_ID from threads where processlist_id=24\G  
  11. *************************** 1. row ***************************  
  12. THREAD_OS_ID: 15020  
  13. 1 row in set (0.00 sec) 

你可以这样操作

  1. pstack 15020 > /tmp/single_pstack.log 

查看pstack结果,只抓取了15020线程的堆栈 

  1. Thread 1 (process 15020):  
  2. #0  0x00007ff1f6368da2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  3. #1  0x0000000001189b53 in native_cond_timedwait (abstime=0x7ff1b810d7f0mutex=0x3b96760 <LOCK_item_func_sleep>cond=0x7ff1b810d8e0) at ../../mysql-8.0.19/include/thr_cond.h:149 
  4. #2  my_cond_timedwait (abstime=0x7ff1b810d7f0mp=0x3b96760 <LOCK_item_func_sleep>cond=0x7ff1b810d8e0) at ../../mysql-8.0.19/include/thr_cond.h:149 
  5. #3  inline_mysql_cond_timedwait (src_file=0x2de5538 "../../mysql-8.0.19/sql/item_func.cc", src_line=4786abstime=0x7ff1b810d7f0mutex=0x3b96760 <LOCK_item_func_sleep>that=0x7ff1b810d8e0) at ../../mysql-8.0.19/include/mysql/psi/mysql_cond.h:217 
  6. #4  Interruptible_wait::wait (thisthis=this@entry=0x7ff1b810d890condcond=cond@entry=0x7ff1b810d8e0mutexmutex=mutex@entry=0x3b96760 <LOCK_item_func_sleep>) at ../../mysql-8.0.19/sql/item_func.cc:4786 
  7. #5  0x0000000001189e26 in Item_func_sleep::val_int (this=<optimized out>) at ../../mysql-8.0.19/sql/item_func.cc:5435 
  8. #6  0x00000000010ff567 in Item::send (this=0x7ff1d45d9380protocol=0x7ff1d4392240buffer=<optimized out>) at ../../mysql-8.0.19/sql/item.cc:6638 
  9. #7  0x0000000000e3d36b in THD::send_result_set_row(List<Item>*) () at ../../mysql-8.0.19/sql/sql_class.cc:2530 
  10. #8  0x000000000127e8f1 in Query_result_send::send_data (this=<optimized out>thd=0x7ff1d457b7b0items=...) at ../../mysql-8.0.19/sql/query_result.cc:93 
  11. #9  0x0000000000f5d29a in SELECT_LEX_UNIT::ExecuteIteratorQuery(THD*) () at ../../mysql-8.0.19/sql/sql_union.cc:1530 
  12. #10 0x0000000000f5f3f9 in SELECT_LEX_UNIT::execute(THD*) () at ../../mysql-8.0.19/sql/sql_union.cc:1588 
  13. #11 0x0000000000eedf8b in Sql_cmd_dml::execute_inner(THD*) () at ../../mysql-8.0.19/sql/sql_select.cc:910 
  14. #12 0x0000000000ef7418 in Sql_cmd_dml::execute (this=0x7ff1d45d9a38thd=0x7ff1d457b7b0) at ../../mysql-8.0.19/sql/sql_select.cc:715  
  15. #13 0x0000000000eab3a9 in mysql_execute_command(THD*, bool) () at ../../mysql-8.0.19/sql/sql_parse.cc:4478  
  16. #14 0x0000000000ead0cc in mysql_parse (thdthd=thd@entry=0x7ff1d457b7b0parser_stateparser_state=parser_state@entry=0x7ff1b810f610) at ../../mysql-8.0.19/sql/sql_parse.cc:5288 
  17. #15 0x0000000000eaeb6b in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at ../../mysql-8.0.19/sql/sql_parse.cc:1777 
  18. #16 0x0000000000eb0104 in do_command (thdthd=thd@entry=0x7ff1d457b7b0) at ../../mysql-8.0.19/sql/sql_parse.cc:1275  
  19. #17 0x0000000000fc1a08 in handle_connection (argarg=arg@entry=0x43cae90) at ../../mysql-8.0.19/sql/conn_handler/connection_handler_per_thread.cc:302  
  20. #18 0x00000000023ffdec in pfs_spawn_thread (arg=0x44c2a30) at ../../../mysql-8.0.19/storage/perfschema/pfs.cc:2854  
  21. #19 0x00007ff1f6364e65 in start_thread () from /lib64/libpthread.so.0  
  22. #20 0x00007ff1f44a188d in clone () from /lib64/libc.so.6 

gdb简介

GDB 是 linux 环境下的一般功能强大的调试器,用来调试 C 或 C++ 写的程序。它可以做这些事情

  •  Start your program, specifying anything that might affect its behavior.
  •  Make your program stop on specified conditions.
  •  Examine what has happened, when your program has stopped.
  •  Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.

常用命令

  •  info threads:查看全部线程
  •  thread n:指定某个线程
  •  b:在某处打断点
  •  c:继续往下走
  •  s:执行一行代码,如果代码函数调用,则进入函数
  •  n:执行一行代码,函数调用不进入
  •  p:打印某个变量值
  •  list:打印代码的文本信息
  •  bt:查看某个线程的栈帧
  •  info b:查看当前所有断点信息

实际案例

进入gdb界面 

  1. [root@VM_0_9_centos ~]# gdb /usr/local/mysql5.7/bin/mysqld 

进入调试 

  1. run --defaults-file=/data/mysql/mysql3308/my3308.cnf --user=mysql --gdb 

通过info thread查看所有线程,这个时候我从另外一个会话连接到了MySQL,我从pfs.threads查寻到这个会话的LWP值为513,那么我可以通过thread 41切换到这个线程 

  1. (gdb) info thread  
  2.   Id   Target Id         Frame  
  3.   41   Thread 0x7fff819cf700 (LWP 513) "mysqld" 0x00007ffff61abbed in poll () from /lib64/libc.so.6  
  4.   40   Thread 0x7fff5affd700 (LWP 416) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  5.   39   Thread 0x7fff81a51700 (LWP 415) "mysqld" 0x00007ffff7bce381 in sigwait () from /lib64/libpthread.so.0  
  6.   38   Thread 0x7fff5b7fe700 (LWP 414) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  7.   37   Thread 0x7fff5bfff700 (LWP 413) "mysqld" 0x00007ffff7bcada2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  8.   36   Thread 0x7fff6cff9700 (LWP 412) "mysqld" 0x00007ffff7bcada2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  9.   35   Thread 0x7fff6d7fa700 (LWP 411) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  10.   34   Thread 0x7fff6dffb700 (LWP 410) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  11.   33   Thread 0x7fff6e7fc700 (LWP 409) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  12.   32   Thread 0x7fff6effd700 (LWP 408) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  13.   31   Thread 0x7fff6f7fe700 (LWP 407) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  14.   30   Thread 0x7fff6ffff700 (LWP 406) "mysqld" 0x00007ffff7bcde5d in nanosleep () from /lib64/libpthread.so.0  
  15.   29   Thread 0x7fff8084e700 (LWP 405) "mysqld" 0x00007ffff7bcada2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  16.   28   Thread 0x7fff8104f700 (LWP 404) "mysqld" 0x00007ffff7bcada2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  17.   27   Thread 0x7fff81850700 (LWP 403) "mysqld" 0x00007ffff7bcada2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  18.   24   Thread 0x7fff74c24700 (LWP 398) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  19.   23   Thread 0x7fff75425700 (LWP 397) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  20.   22   Thread 0x7fff75c26700 (LWP 396) "mysqld" 0x00007ffff7bca9f5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  21.   21   Thread 0x7fff76427700 (LWP 395) "mysqld" 0x00007ffff7bcada2 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0  
  22.   20   Thread 0x7fff76c28700 (LWP 394) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  23.   19   Thread 0x7fff77429700 (LWP 393) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  24.   18   Thread 0x7fff77c2a700 (LWP 392) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  25.   17   Thread 0x7fff7842b700 (LWP 391) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  26.   16   Thread 0x7fff78c2c700 (LWP 390) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  27.   15   Thread 0x7fff7942d700 (LWP 389) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  28.   14   Thread 0x7fff79c2e700 (LWP 388) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  29.   13   Thread 0x7fff7a42f700 (LWP 387) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  30.   12   Thread 0x7fff7ac30700 (LWP 386) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  31.   11   Thread 0x7fff7b431700 (LWP 385) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  32.   10   Thread 0x7fff7bc32700 (LWP 384) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  33.   9    Thread 0x7fff7c433700 (LWP 383) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  34.   8    Thread 0x7fff7cc34700 (LWP 382) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  35.   7    Thread 0x7fff7d435700 (LWP 381) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  36.   6    Thread 0x7fff7dc36700 (LWP 380) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1 
  37.   5    Thread 0x7fff7e437700 (LWP 379) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1 
  38.   4    Thread 0x7fff7ec38700 (LWP 378) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  39.   3    Thread 0x7fff7f439700 (LWP 377) "mysqld" 0x00007ffff79bd644 in __io_getevents_0_4 () from /lib64/libaio.so.1  
  40.   2    Thread 0x7fffec8c4700 (LWP 376) "mysqld" 0x00007ffff60ef53a in sigwaitinfo () from /lib64/libc.so.6  
  41. * 1    Thread 0x7ffff7fe4880 (LWP 371) "mysqld" 0x00007ffff61abbed in poll () from /lib64/libc.so.6  
  42. (gdb) thread 41  
  43. [Switching to thread 41 (Thread 0x7fff819cf700 (LWP 513))]  
  44. #0  0x00007ffff61abbed in poll () from /lib64/libc.so.6 

演示一下打断点: 

  1. (gdb) b do_command  
  2. Breakpoint 1 at 0xc8d060: file /usr/local/src/mysql-5.7.30/sql/sql_parse.cc, line 903.  
  3. (gdb) c  
  4. Continuing.  
  5. (gdb) b do_command  
  6. Breakpoint 1 at 0xc8d060: file /usr/local/src/mysql-5.7.30/sql/sql_parse.cc, line 903.  
  7. (gdb) n  
  8. Single stepping until exit from function poll,  
  9. which has no line number information.  
  10. vio_io_wait (viovio=vio@entry=0x54fd330eventevent=event@entry=VIO_IO_EVENT_READtimeout=<optimized out>) at /usr/local/src/mysql-5.7.30/vio/viosocket.c:815  
  11. 815       MYSQL_END_SOCKET_WAIT(locker, 0);  
  12. (gdb) n  
  13. 817     }  
  14. (gdb) n  
  15. vio_socket_io_wait (viovio=vio@entry=0x54fd330eventevent=event@entry=VIO_IO_EVENT_READ) at /usr/local/src/mysql-5.7.30/vio/viosocket.c:92  
  16. 92          ret= -1;  
  17. (gdb) n  
  18. 105     }  
  19. (gdb) n  
  20. vio_read (vio=0x54fd330buf=0x54e3260 "\001", size=4) at /usr/local/src/mysql-5.7.30/vio/viosocket.c:134  
  21. 134       while ((retmysql_socket_recv(vio->mysql_socket, (SOCKBUF_T *)buf, size, flags)) == -1)  
  22. (gdb) n  
  23. 148     }  
  24. (gdb) n  
  25. net_read_raw_loop (netnet=net@entry=0x54841e8countcount=count@entry=4) at /usr/local/src/mysql-5.7.30/sql/net_serv.cc:697  
  26. 697         if (recvcnt == VIO_SOCKET_ERROR)  
  27. (gdb) n  
  28. 706         else if (!recvcnt)  
  29. (gdb) n  
  30. 715         thd_increment_bytes_received(recvcnt);  
  31. (gdb) n  
  32. 712         count-recvcnt 
  33. (gdb) n  
  34. 713         buf+= recvcnt;  
  35. (gdb) n  
  36. 715         thd_increment_bytes_received(recvcnt);  
  37. (gdb) n  
  38. 692       while (count)  
  39. (gdb) n  
  40. 737     }  
  41. (gdb) n  
  42. 736       return MY_TEST(count);  
  43. (gdb) n  
  44. 737     }  
  45. (gdb) n  
  46. net_read_packet_header (net=0x54841e8) at /usr/local/src/mysql-5.7.30/sql/net_serv.cc:779  
  47. 779         server_extension->m_after_header(net, user_data, count, rc);  
  48. (gdb) n  
  49. 778         rcnet_read_raw_loop(net, count);  
  50. (gdb) n  
  51. 779         server_extension->m_after_header(net, user_data, count, rc);  
  52. (gdb) n  
  53. 787       if (rc)  
  54. (gdb) n  
  55. 792       pkt_nrnet->buff[net->where_b + 3];  
  56. (gdb) n  
  57. 798       if (pkt_nr != (uchar) net->pkt_nr)  
  58. (gdb) n  
  59. 818       net->pkt_nr++;  
  60. (gdb) n  
  61. net_read_packet (net=0x54841e8complen=0x7fff819cec00) at /usr/local/src/mysql-5.7.30/sql/net_serv.cc:850  
  62. 850       if (net->compress)  
  63. (gdb) n  
  64. 844       if (net_read_packet_header(net)) 
  65. (gdb) n  
  66. 847       net->compress_pkt_nrnet->pkt_nr;  
  67. (gdb) n  
  68. 850       if (net->compress)  
  69. (gdb) n  
  70. 868       pkt_lenuint3korr(net->buff+net->where_b);  
  71. (gdb) n  
  72. 871       if (!pkt_len)  
  73. (gdb) n  
  74. 868       pkt_lenuint3korr(net->buff+net->where_b);  
  75. (gdb) n  
  76. 871       if (!pkt_len)  
  77. (gdb) p pkt_len  
  78. $1 = 22  
  79. (gdb) n  
  80. 874       pkt_data_len = max(pkt_len, *complen) + net->where_b;  
  81. (gdb) n  
  82. 877       if ((pkt_data_len >= net->max_packet) && net_realloc(net, pkt_data_len))  
  83. (gdb) n  
  84. 881       if (net_read_raw_loop(net, pkt_len))  
  85. (gdb) n  
  86. 868       pkt_lenuint3korr(net->buff+net->where_b);  
  87. (gdb) n  
  88. 886       return pkt_len;  
  89. (gdb) n  
  90. 891     }  
  91. (gdb) n  
  92. my_net_read (net=0x54841e8) at /usr/local/src/mysql-5.7.30/sql/net_serv.cc:922  
  93. 922         if (len == MAX_PACKET_LENGTH)  
  94. (gdb) n  
  95. 925           ulong save_pos = net->where_b;  
  96. (gdb) n  
  97. 922         if (len == MAX_PACKET_LENGTH)  
  98. (gdb) n  
  99. 937         net->read_pos = net->buff + net->where_b;  
  100. (gdb) n  
  101. 938         if (len != packet_error)  
  102. (gdb) n  
  103. 937         net->read_pos = net->buff + net->where_b;  
  104. (gdb) n  
  105. 938         if (len != packet_error)  
  106. (gdb) n  
  107. 939           net->read_pos[len]=0;             /* Safeguard for mysql_use_result */  
  108. (gdb) n  
  109. 1056      MYSQL_NET_READ_DONE(0, len);  
  110. (gdb) n  
  111. 1058    }  
  112. (gdb) n  
  113. Protocol_classic::read_packet (this=0x5483a88) at /usr/local/src/mysql-5.7.30/sql/protocol_classic.cc:820  
  114. 820         raw_packetm_thd->net.read_pos;  
  115. (gdb) n  
  116. 819         bad_packetfalse 
  117. (gdb) n  
  118. 820         raw_packetm_thd->net.read_pos;  
  119. (gdb) n  
  120. 829     }  
  121. (gdb) n  
  122. 820         raw_packetm_thd->net.read_pos;  
  123. (gdb) n  
  124. 829     }  
  125. (gdb) n  
  126. Protocol_classic::get_command (this=0x5483a88com_data=0x7fff819cecc0cmd=0x7fff819cecb0) at /usr/local/src/mysql-5.7.30/sql/protocol_classic.cc:983  
  127. 983       if (packet_length == 0)                       /* safety */  
  128. (gdb) n  
  129. 990       raw_packet[packet_length]= '\0';                  /* safety */  
  130. (gdb) n  
  131. 1002      return parse_packet(com_data, *cmd);  
  132. (gdb) n  
  133. 990       raw_packet[packet_length]= '\0';                  /* safety */  
  134. (gdb) n  
  135. 992       *cmd= (enum enum_server_command) raw_packet[0];  
  136. (gdb) n  
  137. 994       if (*cmd >= COM_END)  
  138. (gdb) n  
  139. 992       *cmd= (enum enum_server_command) raw_packet[0];  
  140. (gdb) n  
  141. 1002      return parse_packet(com_data, *cmd);  
  142. (gdb) n  
  143. 999       packet_length--;  
  144. (gdb) n  
  145. 1000      raw_packet++;  
  146. (gdb) n  
  147. 1002      return parse_packet(com_data, *cmd);  
  148. (gdb) n  
  149. 1003    }  
  150. (gdb) n  
  151. do_command (thdthd=thd@entry=0x5482a30) at /usr/local/src/mysql-5.7.30/sql/sql_parse.cc:974  
  152. 974       if (rc)  
  153. (gdb) s  
  154. 971       rcthd->get_protocol()->get_command(&com_data, &command);  
  155. (gdb) bt  
  156. #0  do_command (thdthd=thd@entry=0x5482a30) at /usr/local/src/mysql-5.7.30/sql/sql_parse.cc:971 
  157. #1  0x0000000000d4abf8 in handle_connection (argarg=arg@entry=0x5418ff0) at /usr/local/src/mysql-5.7.30/sql/conn_handler/connection_handler_per_thread.cc:313 
  158. #2  0x000000000120a451 in pfs_spawn_thread (arg=0x5452410) at /usr/local/src/mysql-5.7.30/storage/perfschema/pfs.cc:2197 
  159. #3  0x00007ffff7bc6e65 in start_thread () from /lib64/libpthread.so.0  
  160. #4  0x00007ffff61b688d in clone () from /lib64/libc.so.6  
  161. (gdb) s  
  162. 972       thd->m_server_idlefalse 
  163. (gdb) s  
  164. 974       if (rc)  
  165. (gdb) s  
  166. 1027          thd->variables.net_buffer_length);  
  167. (gdb) s  
  168. Protocol_classic::get_packet (this=0x5483a88) at /usr/local/src/mysql-5.7.30/sql/protocol_classic.cc:807  
  169. 807     {  
  170. (gdb) bt  
  171. #0  Protocol_classic::get_packet (this=0x5483a88) at /usr/local/src/mysql-5.7.30/sql/protocol_classic.cc:807  
  172. #1  0x0000000000c8d1b3 in do_command (thdthd=thd@entry=0x5482a30) at /usr/local/src/mysql-5.7.30/sql/sql_parse.cc:1027  
  173. #2  0x0000000000d4abf8 in handle_connection (argarg=arg@entry=0x5418ff0) at /usr/local/src/mysql-5.7.30/sql/conn_handler/connection_handler_per_thread.cc:313  
  174. #3  0x000000000120a451 in pfs_spawn_thread (arg=0x5452410) at /usr/local/src/mysql-5.7.30/storage/perfschema/pfs.cc:2197  
  175. #4  0x00007ffff7bc6e65 in start_thread () from /lib64/libpthread.so.0  
  176. #5  0x00007ffff61b688d in clone () from /lib64/libc.so.6 

我们可以在调试过程中随时打印某个变量的值,例如: 

  1. (gdb) p pkt_len  
  2. $1 = 22 

甚至gdb可以在紧急情况下救你一命,例如,当MySQL数据库连接打满又没有后台线程可以连接到MySQL的时候,你可以通过gdb来修改MySQL的连接数,例如 

  1. gdb -p $(pidof mysqld) -ex "set max_connections=1500" -batch 

总结一下:

  •  本文内容比较多,给大家介绍了几个MySQL DBA(可能其他岗位同样适用)应该掌握的必备工具
  •  strace可以用来跟踪某个线程的调用情况,例如可以适用strace跟踪客户端SQL执行情况,如果开发说有很多慢SQL,而MySQL却没有任何记录,那么就可以用strace来跟踪把锅甩给开发(案例来自有赞王航威)
  •  pstack是平时用的比较多的工具,尤其是诊断MySQL hang住的情况,例如主从延迟特别高等
  •  gdb可能用的不是太多,但是我认为是MySQL DBA进阶必会的技能之一,尤其是抽丝剥茧某些疑难case的时候非常有用 

 

责任编辑:庞桂玉 来源: 老叶茶馆
相关推荐

2020-08-13 08:45:09

多线程死锁

2023-08-24 22:13:31

2022-02-20 21:35:43

MySQLDDL阻塞

2009-07-16 17:06:55

JSP网页中JDBC代

2012-08-13 10:16:34

IBMdW

2019-11-07 15:02:00

Linuxstrace系统调用

2014-08-12 11:21:32

2013-12-27 10:37:01

2016-03-01 14:37:38

iOSLLDB调试技巧

2021-08-25 23:03:58

区块链数据安全

2009-07-19 10:01:37

linuxlinux安全后门

2021-04-07 10:38:43

MySQL数据库命令

2019-05-23 08:08:33

MySQL数据库DBA

2021-04-23 21:03:10

MySQL数据语法

2024-03-07 13:35:44

数字鸿沟智慧城市人工智能

2016-10-27 08:39:35

大数据设计定量

2021-12-13 09:13:48

网站数据客户数据

2023-07-26 06:43:07

函数调用

2014-03-28 09:45:14

科来软件网络分析

2019-11-05 08:24:34

JavaOOM快速定位
点赞
收藏

51CTO技术栈公众号