在这篇文章中,我想探索一种与MySQL建立100,000个连接的方法。不只是空闲连接,而是执行查询。
你真的需要MySQL100,000个连接,你可能会问?虽然看起来有点过分,但我在客户部署中看到了很多不同的设置。有些部署了一个应用程序连接池,每个池中有100个应用程序服务器和1,000个连接。有些应用程序使用“如果查询太慢,会重新尝试连接”的技术,这是一种可怕的做法。它可以导致滚雪球效应,并可以在几秒钟内建立数千个与MySQL的连接。
所以现在我想设定一个超出预期的目标,看看我们是否能够实现它。
构建
我将使用以下硬件:
由packet.net提供的裸机服务器,实例大小:c2.medium.x86
物理内核@ 2.2 GHz
(1 X AMD EPYC 7401P)
内存:64 GB ECC RAM
存储:INTEL SSDDC S4500,480GB
这是服务器级SATA SSD。
我将使用其中五个盒子,原因如下。一个用于MySQL服务器的盒子和四个用于客户端连接的盒子。
对于服务器,我将使用带有线程池插件的Percona Server for MySQL 8.0.13-4。该插件将需要支持数千个连接。
初始服务器设置
网络设置(Ansible格式):
- { name: 'net.core.somaxconn', value: 32768 } |
这些是推荐用于10Gb网络和高并发工作负载的典型设置。
限制systemd的设置:
[Service] |
以及my.cnf中MySQL的相关设置:
back_log=3500 |
对于客户端,我将使用sysbench版本0.5而不是1.0.x,原因如下所述。
发出工作负载:
sysbench --test=sysbench/tests/db/select.lua --mysql-host=139.178.82.47 --mysql-user=sbtest --mysql-password=sbtest --oltp-tables-count=10 --report-interval=1 --num-threads=10000 --max-time=300 --max-requests=0 --oltp-table-size=10000000 --rand-type=uniform --rand-init=on run |
1. 设置10,000个连接
这个很容易,因为没有太多的事要做。我们只用一个客户就可以做到这一点。但是您可能在客户端遇到以下错误:
FATAL: error 2004: Can't create TCP/IP socket (24)
这是由打开文件限制引起的,这也是TCP / IP套接字的限制。这可以通过设置:
ulimit -n 100000
我们观察到性能:
[ 26s] threads: 10000, tps: 0.00, reads: 33367.48, writes: 0.00, response time: 3681.42ms (95%), errors: 0.00, reconnects: 0.00 |
2. 设置25,000个连接
当有25,000个连接,我们在MySQL端遇到错误:
Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug |
如果您尝试查找有关此错误的信息,可能会发现以下文章:https: //www.percona.com/blog/2013/02/04/cant_create_thread_errno_11/
但是在我们的情况下它并没有帮助,因为我们已将所有限制设置得足够高:
cat /proc/`pidof mysqld`/limits |
在我们开始使用线程池功能的地方:https: //www.percona.com/doc/percona-server/8.0/performance/threadpool.html 加上:
thread_handling = pool - of - threads
到my.cnf并重启Percona Server,结果:
[ 7s] threads: 25000, tps: 0.00, reads: 33332.57, writes: 0.00, response time: 974.56ms (95%), errors: 0.00, reconnects: 0.00 |
我们有相同的吞吐量,但实际上95%的响应时间已经从3690毫秒改善到979毫秒(由于线程池)。
3. 50,000个连接
这是我们遇到的最大挑战。首先,尝试在sysbench中获取50,000个连接,我们遇到以下错误:
FATAL: error 2003: Can't connect to MySQL server on '139.178.82.47' (99)
Error (99) 是神秘的,它意味着:无法分配请求的地址。
它来自应用程序可以打开的端口限制。默认情况下,我的系统是
cat /proc/sys/net/ipv4/ip_local_port_range : 32768 60999 |
这表示只有28,231个可用端口--60999减去32768 - 或者您可以与给定IP地址建立或建立TCP连接的限制。您可以在客户端和服务器上使用更广泛的范围扩展它:
echo 4000 65000 > /proc/sys/net/ipv4/ip_local_port_range |
这将为我们提供61,000个连接,但这非常接近一个IP地址的限制(最大端口为65535)。这里的关键点是,如果我们想要更多的连接,我们需要为MySQL服务器分配更多的IP地址。为了实现100,000个连接,我将在运行MySQL的服务器上使用两个IP地址。
在整理出端口范围后,我们遇到了sysbench的以下问题:
sysbench 0.5: multi-threaded system evaluation benchmark |
在这种情况下,这是sysbench内存分配(即lua子系统)的问题。Sysbench只能为32,351个连接分配内存。这是一个在sysbench 1.0.x中更严重的问题。
Sysbench 1.0.x限制
Sysbench 1.0.x使用不同的Lua JIT,即使有4000个连接也会遇到内存问题,所以在sysbench 1.0.x中不可能超过4000连接
因此,与Percona Server相比,我们似乎比sysbench更快地达到了极限。为了使用更多连接,我们需要使用多个sysbench客户端,如果32,351连接是sysbench的限制,我们必须使用至少四个sysbench客户端来获得多达100,000个连接。
对于50,000个连接,我将使用2个服务器(每个服务器运行单独的sysbench),每个服务器运行来自sysbench的25,000个线程。
每个sysbench的结果如下所示:
[ 29s] threads: 25000, tps: 0.00, reads: 16794.09, writes: 0.00, response time: 1799.63ms (95%), errors: 0.00, reconnects: 0.00 |
所以我们有相同的吞吐量(总共16794 * 2 = 33588 tps),但95%的响应时间翻了一番。这是预料之中的,因为与25,000个连接基准测试相比,我们使用的连接数是原来的两倍。
3. 75,000个连接
要实现75,000个连接,我们将使用三个带sysbench的服务器,每个服务器运行25,000个线程。
每个sysbench的结果:
[ 157s] threads: 25000, tps: 0.00, reads: 11633.87, writes: 0.00, response time: 2651.76ms (95%), errors: 0.00, reconnects: 0.00 |
4. 100,000个连接
实现75k和100k连接没有任何意义。我们只需启动一个额外的服务器并启动sysbench。对于100,000个连接,我们需要四个服务器用于sysbench,每个服务器显示:
[ 101s] threads: 25000, tps: 0.00, reads: 8033.83, writes: 0.00, response time: 3320.21ms (95%), errors: 0.00, reconnects: 0.00 |
因此我们具有相同的吞吐量(总共8065 * 4 = 32260 tps),响应时间为3405毫秒95%。
这是一个非常重要的要点:使用100k连接并使用线程池,95%的响应时间甚至比没有线程池的10k连接更好。线程池允许Percona Server更有效地管理资源并提供更好的响应时间。
结论
MySQL可以实现100k连接,我相信我们可以更进一步。有三个组件可以实现此目的:
- Percona Server中的线程池
- 正确调整网络限制
- 在服务器盒上使用多个IP地址(每个约60k连接一个IP地址)
完整my.cnf:
[mysqld] |