GreatSQL连接数被打满的3种紧急解决方案
<h1 id="greatsql连接数被打满的3种紧急解决方案">GreatSQL连接数被打满的3种紧急解决方案</h1><h2 id="背景">背景</h2>
<p>使用数据库时,偶尔会出现数据库连接数飙升的场景,最严重的情况是连接打满,root 用户无法获取到连接,登陆数据库失败。这时候无法登录数据库kill 掉相关的数据连接,影响数据库的稳定性。下面将对这类的故障的处理进行详述。</p>
<h2 id="场景复现">场景复现</h2>
<ol>
<li>设置<code>max_connections</code> 为500</li>
</ol>
<pre><code class="language-SQL">greatsql> SET GLOBAL max_connections=500;
Query OK, 0 rows affected (0.00 sec)
greatsql> SHOW variables LIKE '%max_connections%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| max_connections | 500 |
+------------------------+-------+
1 rows in set (0.01 sec)
</code></pre>
<ol start="2">
<li>使用sysbench 模拟并发连接</li>
</ol>
<pre><code class="language-SQL">准备测试数据
$ sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=yourusername --mysql-password=yourpassword --mysql-db=sysbench_test --tables=10 --table-size=10000 prepare
执行测试
$ sysbench oltp_read_write --mysql-host=127.0.0.1--mysql-port=3306 --mysql-user=yourusername --mysql-password=yourpassword --mysql-db=sysbench_test --tables=10 --table-size=10000 --time=600 --threads=500 --report-interval=10 run
</code></pre>
<ol start="3">
<li>尝试登录数据库报错</li>
</ol>
<pre><code class="language-SQL">$ /greatsql/svr/greatsql/bin/mysql -h127.0.0.1 -uroot -p
ERROR 1040 (HY000): Too many connections
</code></pre>
<h2 id="场景分析">场景分析</h2>
<h3 id="方案1使用-admin_port-进行故障处理">方案1:使用 admin_port 进行故障处理</h3>
<p>从GreatSQL 8.0起,GreatSQL 支持在配置文件配置<code>admin_port</code>和<code>admin_address</code>,这两个参数都是静态参数,不支持动态修改。<code>admin_address</code>没有默认值,如果没有显式开启,GreatSQL 不会维护任何的管理接口。<code>admin_port</code>即为管理接口连接TCP/IP的端口号,默认值为33062。</p>
<blockquote>
<p>注意如果<code>admin_address</code>未设置,<code>admin_port</code>也将无效。</p>
</blockquote>
<ol>
<li>查看配置文件是否配置了<code>admin_address</code>和<code>admin_port</code></li>
</ol>
<pre><code class="language-Bash">$cat /greatsql/conf/greatsql.cnf | grep admin
admin_address='127.0.0.1'
admin_port=3806
</code></pre>
<ol start="2">
<li>使用<code>admin_port</code>和<code>admin_address</code>尝试登录数据库,登录成功。</li>
</ol>
<pre><code class="language-SQL">$ /greatsql/svr/greatsql/bin/mysql -h127.0.0.1 -uroot -p -P3806
greatsql > SELECT count(*),User FROM information_schema.processlist GROUP BY 2 ;
+----------+-----------------+
| count(*) | User |
+----------+-----------------+
| 1 | root |
| 500 | test_user |
+----------+-----------------+
1 row in set (0.00 sec)
</code></pre>
<ol start="3">
<li>使用kill 将特定条件的连接杀掉,如果处于业务高峰期,也可以先使用SET GLOBAL max_connections= 临时调大连接数,要注意此时资源的负载情况。 下面表示的是查找出语句正在运行中且执行时间超过100s,用户叫test_user 的id 并拼装成kill 语句。请结合实际场景进行条件调整。</li>
</ol>
<pre><code class="language-SQL">greatsql> SELECT concat("kill ",id,";") FROM information_schema.processlist
WHERE info IS NOT NULL
AND command != 'sleep'
AND time>100
AND USER ='test_user';
</code></pre>
<h3 id="方案2gdb-在线关闭-tcp-socket">方案2:GDB 在线关闭 TCP SOCKET</h3>
<p>上述演示是最为顺利的情况,但是不妨假设,如果未设置<code>admin_address</code> 和<code>admin_port</code> ,难道只有剩下数据库重启这种方式了吗?</p>
<p>其实不然,GreatSQL 默认使用 TCP/IP 进行网络连接,那是否可以把实例上由远程机器请求的部分 TCP socket 连接 kill 掉,但保持数据库进程的运行,以腾出部分连接数供 root 用户登录呢?</p>
<p>通过查阅资料, <code>gdb attach</code> 刚好能满足我们的设想:<code>gdb attach</code> 是 GDB(GNU 调试器)中的一个命令,用于<strong>附加(Attach)到一个正在运行的进程</strong>,而关闭一个 SOCKET,只要调用 close 函数就可以了。简单来说就是使用<code>gdb attach</code> 到进程上下文,然后 <code>call close($fd)</code>。</p>
<p>不过需要注意的是,<code>gdb attach</code> 会暂停目标进程的所有线程。对于生产环境中需要持续运行的进程(如服务、数据库、实时系统等),这种暂停可能导致服务中断或超时。且gdb 需要较大的性能开销,进程的运行速度会显著下降,需要经过谨慎评估后再使用此方式。</p>
<p>继续复现一个连接数打满,但是未启用<code>admin_address</code>和 <code>admin_port</code> 的场景,此时尝试用管理员登录报错ERROR 1040 (HY000): Too many connections 。</p>
<ol>
<li>通过 netstat 反查数据库的进程号为18979</li>
</ol>
<pre><code class="language-Bash">$ netstat -nltp | grep 3306
tcp6 0 0 :::3306 :::* LISTEN 18979/mysqld
</code></pre>
<ol start="2">
<li>使用 lsof 找到进程号18979的文件描述,并找到对应的 socket</li>
</ol>
<pre><code class="language-Bash">$ lsof -np 18979
COMMANDPID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 18979 greatsqlcwd DIR 8,2 40964971025 /greatsql/dbdata/data3306/data
mysqld 18979 greatsqlrtd DIR 8,2 4096 2 /
mysqld 18979 greatsqltxt REG 8,2 12414251044971271 /greatsql/svr/GreatSQL-8.0.32-27-Linux-glibc2.17-x86_64/bin/mysqld
mysqld 18979 greatsqlmem REG 8,2 37216 688174 /usr/lib64/libnss_sss.so.2
...
mysqld 18979 greatsql371u IPv6 31132193 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40694 (ESTABLISHED)
mysqld 18979 greatsql372u IPv6 31132194 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40698 (ESTABLISHED)
mysqld 18979 greatsql373u IPv6 31132195 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40700 (ESTABLISHED)
mysqld 18979 greatsql374u IPv6 31132196 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40702 (ESTABLISHED)
mysqld 18979 greatsql375u IPv6 31132197 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40704 (ESTABLISHED)
mysqld 18979 greatsql376u IPv6 31132198 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40706 (ESTABLISHED)
mysqld 18979 greatsql377u IPv6 31132201 0t0 TCP 172.17.134.208:mysql->172.17.139.57:40708 (ESTABLISHED)
...
</code></pre>
<p>其中NODE NAME 有(ESTABLISHED)表示两台主机TCP连接已经成功建立。找到对应的 FD,并记录下来</p>
<ol start="3">
<li>使用 gdb 连接到进程,并关闭 socket 连接</li>
</ol>
<pre><code class="language-Bash">$ gdb -p 18979
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 Free Software Foundation, Inc
....
Loaded symbols for /greatsql/svr/greatsql/lib/mysql/libjemalloc.so
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
$ (gdb) call close(371u)
$1 = 0
$ (gdb) call close(372u)
$1 = 0
....
</code></pre>
<ol start="4">
<li>查看数据库进程,并再次尝试登录数据库,登录成功。</li>
</ol>
<pre><code class="language-Bash">$ ps -ef | grep mysql
greatsql18979 1 99 13:30 ? 00:48:41 /greatsql/svr/greatsql/bin/mysqld --defaults-file=/greatsql/conf/greatsql.cnf
</code></pre>
<h3 id="方案3预防性设置-max_user_connections">方案3:<strong>预防性设置 max_user_connections</strong></h3>
<p>以上的情况为出现问题的时候的紧急处理,但作为DBA,保障线上稳定性为第一前提,连接数打满已经是一个较为糟糕的情况了。且前文描述的是数据库大版本为 8.0 后才开始支持<code>admin_address</code> ,<code>admin_port</code>作为突发情况的紧急处理。但如果GreatSQL 5.7 或者更低的情况, 如何去保障生产的稳定性呢?</p>
<p>GreatSQL 还有一个参数max_user_connections,我们来比较<code>max_user_connections</code>和<code>max_connections</code> 的区别</p>
<ul>
<li><strong>max_connections:代表允许连接数据库的所有用户的连接数总和</strong></li>
<li><strong>max_user_connections:代表允许单个用户的连接数最大值,即并发值</strong></li>
</ul>
<p>出故障的时候,往往是同一个用户频繁的申请连接,那如果我们把单个用户的最大连接数调整到比最大连接数再小一点的值,确保管理员账号有足够的连接数进行突发故障的处理。 也可以有效减少连接打满的情况。且可以动态调整,可以使用SET GLOBAL max_user_connections=生效。除此之外,该参数可针对特定用户设置 :如<code>ALTER USER 'test_user'@'%' WITH MAX_USER_CONNECTIONS 100;</code></p>
<h2 id="总结">总结</h2>
<ol>
<li>无论对于5.7 还是8.0,建议设置<code>max_user_connections</code>降低连接数打满的风险</li>
<li>对于8.0 版本,可以通过配置增加<code>admin_address</code> 和<code>admin_port</code>来启用管理接口,以应对突发的情况。</li>
<li>如果出现连接数打满且未启用管理端口的情况,可以使用<code>gdb attach</code>在线关掉部分socket,以避免数据库的重启。但需要注意<code>gdb attach</code> 对机器性能的开销和gdb 运行时数据库的所有线程都会暂停,请评估后再使用该方式。</li>
<li>监控可以加上对连接数的监控,达到阈值时告警出来,提前介入处理。</li>
<li>事后应排查连接数打满的原因,和业务协调优化措施,建议采用线程池等技术提前规避连接数飙高的场景。</li>
</ol>
<hr>
<p>Enjoy GreatSQL 😃</p>
<h2 id="关于-greatsql">关于 GreatSQL</h2>
<p>GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。</p>
<p>相关链接: GreatSQL社区 Gitee GitHub Bilibili</p>
<h2 id="greatsql社区">GreatSQL社区:</h2>
<blockquote>
<p>社区博客有奖征稿详情:https://greatsql.cn/thread-100-1-1.html</p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/other/2630741/202506/2630741-20250604100659949-1687089935.png"></p>
<h2 id="技术交流群">技术交流群:</h2>
<blockquote>
<p>微信:扫码添加<code>GreatSQL社区助手</code>微信好友,发送验证信息<code>加群</code>。</p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/other/2630741/202506/2630741-20250604100700279-776013019.png"></p><br><br>
来源:https://www.cnblogs.com/greatsql/p/18909583
頁:
[1]