sysbench benchmark MySQL 时候,为什么 kill 连接后,sysbench 没有重连
文章简介:探索为什么 sysbench
没有重连 Reconnect
背景
plantegg 提供了一个案例,大体上是阿里云上 sysbench 压测 MySQL 的时候,使用 MySQL 客户端 kill sysbench 发起的压测连接,发现 sysbench 没有重连,QPS 始终为 0。plantegg 给出了复现方法。
问题复现
在我的 M1 mac 上使用 vagrant + vmware_fusion 启动了一个虚拟机,Vagrantfile 如下,虚拟机启动的环境内核版本更可控;
|
|
|
|
复现后的现象:
sysbench 吞吐跌零:
|
|
网络连接大量 CLOSE_WAIT:
|
|
sysbench 的 cpu 占用 67%,因为是一个线程在跑,已经比较高了。
分析一下原因
先看看 cpu 到被消耗在了内核还是进程
|
|
可以看到所有的 cpu 都在内核态。实际跑下来,刚复现跌零时候 cpu 比较少,随着 TCP 连接的变多,内核 cpu 越来越高。
看看内核在干什么:
|
|
__inet_check_established, inet_ehashfn 内核函数执行占用过多 cpu
可以看到 __inet_check_established 和 inet_ehashfn 两个函数占用比较高;
TODO: 需要研究一下 kernel 函数 __inet_check_established 和 inet_ehashfn 性能消耗在了哪里。
现在看看进程在做什么事情:
|
|
可以看到在反复的循环的 syscall getpid->socket->connect->clock_nanosleep
;
|
|
syscall 的调用时间消耗在了 clock_nanosleep 和 connect 上。到这里,基本可以猜测程序在循环执行 connect 和 clock_nanosleep。
|
|
可以看到当前调用的第三方库的函数是,mysql connect 跟 syscall 中的 connect 和 clock_nanosleep 也是匹配的;
尝试看看能不能抓到程序执行流程大概是怎样的
|
|
打开 sysbench 的 debug 日志,看看有没有方便定位到代码行的信息
|
|
结合下载的代码,可以看到 DEBUG: Reconnecting
而未输出 DEBUG: Reconnected
,可以确认 sysbench 在执行 mysql_drv_reconnect 后边这个循环。再结合上述的调用栈,可以确认调用路径。
|
|
|
|
去翻了下 mariadb 跟 Mysql mysql_real_connect 的文档发现 mariadb 的文档 中有一句话:
The connection handle can’t be reused for establishing a new connection. It must be closed and reinitialized before.
而 Mysql 的文档中对应的章节并未提到。
直接去掉 mysql_close(con) 去掉后使用 mariadb 的依赖重新编译发现,kill 链接后 QPS 能够自动恢复。
根据其他人的实验报告,大搞如此执行的原因是 mariadb-connect-c 的库函数参数兼容,但是行为跟 mysql 的客户端行为有差异导致的。
比起如何修复更想关注为什么再循环执行这个函数
具体是如何工作的可以看 [这里](./docs/999我的sysbench 压测MySQL时 kill 链接后为什么不会重连?.htm)
TODO: gdb debug 试试看具体的执行流程及各参数返回值,确认如此执行的原因。
问题
为什么 MySQL 8.4.2 有的 Recv-Q 是 28,有的是 132
|
|
TODO: 详细学习一下 Recv-Q Send-Q 分别意味着发生了什么事情
尝试抓 mysqld 的 syscall 发现,正常的登录过程,sendto 发送了 77(正常登录回显)+54(超时回显)+1(close 后的 fin 占用一个 byte)=132
|
|
尝试抓 mysqld 的 syscall 发现,连接过多时,sendto 发送了 27(too many conn)+1(close 后的 fin 占用一个 byte)=28
|
|
为什么 CPU 这么高,CPU 都在忙什么
perf top 可以看到,kernel 在执行 __inet_check_established;
|
|
|
|
strace 可以看出 sysbench 在执行 connect,但是一直 errors,且可以看到几个 syscall calls 相差不大,echo $((4518-4491)) = 27
什么原因会导致 CLOSE_WAIT 状态
从 wiki 中搬一张图:
被动关闭方接收到 fin 后,没有发送 fin,被动关闭方状态停留在 CLOSE_WAIT。此例子中 CLOSE_WAIT 出现在 sysbench 中,说明是由于 mysql server 关闭发起 close,而客户端被动关闭未 close,客户端(sysbench)连接状态为 CLOSE_WAIT;
在 sysbench 机器中执行 nc -vz ip 3306
,报错 Cannot assign requested address.
也说明是 sysbench 的问题。
|
|
mysql 这里在做什么事情?mysql 多久会触发 close 行为呢?
可以看到,mysql 在 0.00618s mysql 返回了登录验证的 prompt,此时 mysql 中的连接状态是:
10.008458 mysql 返回超时。说明 mysql 这里的登录超时时间是 10 s。
后续超时的连接被内核 reset 掉。
为什么 Sysbench 要疯狂创建 4 万多个连接
TODO: 需要翻翻 sysbench 的代码,确认一下原因了,看到 syscall 中大量的 connect,大概是到了一个死循环处
TODO: perf top 中展开可以看到很多函数地址,并无执行的函数名字,猜测是 sysbench 的函数,需要编译 sysbench 后确认一下
TODO: EADDRNOTAVAIL; //Cannot assign requested address 这个错误的含意是什么,何时出现