如何巧妙解决 Too many connections 报错?

用 GDB 工具解决 MySQL 连接数打满问题

1. 背景

在日常的 MySQL 运维中,难免会出现参数设置不合理,导致 MySQL 在使用过程中出现各种各样的问题。

今天,我们就来讲解一下 MySQL 运维中一种常见的问题:最大连接数设置不合理,一旦到了业务高峰期就会出现连接数打满的问题。

报错信息

ERROR 1040 (HY000): Too many connections

报错原因

这个问题,无非就是并发过高,导致最大连接数被用完引起的。

解决办法

重新设置最大连接数 max_connections 的值。

处理痛点

  1. 出现了 Too many connections 的报错信息,无法用 mysql 客户端连接进行动态修改。
  2. 修改配置参数后重启,确实可以让 max_connections 的值重新生效,但是生产环境有业务访问,肯定是不能随便重启的。

2. GDB 工具

基于以上种种问题和主要痛点,那么我们就来试试 gdb 工具吧,让我们在不重启的状态下,照样可以修改 MySQL 的参数 !!!

gdb 工具的介绍在这里就不做过多赘述了,其实大多数场景下,都是把它当做调试工具来用。今天我们更多的是用到 gdb 在线修改配置参数的功能。

3. 处理过程

下面,我们就通过一个实验场景,来模拟 mysql 连接数打满,出现 Too many connections 的问题,通过 gdb 工具的解决步骤。

  1. 准备一个数据库实例,数据库版本为 MySQL 5.7.40。
  2. 创建一个空库 jiangshifeng
mysql> create database jiangshifeng;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| jiangshifeng       |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> use jiangshifeng;
Database changed
mysql> show tables;
Empty set (0.00 sec)
  1. 设置该实例的最大连接数为 10。
mysql> set global max_connections=10;
Query OK, 0 rows affected (0.01 sec)

mysql> show variables like '%max_conn%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| max_connect_errors | 100   |
| max_connections    | 10    |
+--------------------+-------+
2 rows in set (0.00 sec)
  1. 安装 gdb 工具。
yum install -y gdb

安装完成验证

[root@node1 ~]# gdb
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) 
(gdb) q
[root@node1 ~]# 
  1. 使用 sysbench 进行压测,模拟在 jiangshifeng 这个空库创建 11 张表,每个表的数据量为 20000000 条,连接线程数为 15。
sysbench /usr/share/sysbench/oltp_common.lua --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password=123456 --mysql-db=jiangshifeng --db-driver=mysql --tables=11 --table-size=20000000 --report-interval=15 --threads=15  prepare
  1. 此时,我们再次用客户端工具连接 mysql,就会报错(Too many connections)。
[root@node1 ~]# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1040 (HY000): Too many connections
[root@node1 ~]#
  1. 通过 gdb 工具设置 max_connections 参数。

方法一

gdb -p $(pidof mysqld) -ex "set max_connections=50" -batch

方法二

[root@node1 ~]# ps -ef | grep mysql
root       1320      1  0 Mar17 ?        00:00:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/DB/mysql --pid-file=/DB/mysql/node1.pid
mysql      1582   1320  9 Mar17 ?        03:33:44 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/DB/mysql --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=node1.err --pid-file=/DB/mysql/node1.pid --socket=/tmp/mysql.sock --port=3306

[root@node1 ~]# gdb -p 1582
...
(gdb) p max_connections
$1 = 10
(gdb) 
(gdb) set max_connections=50
(gdb) p max_connections
$4 = 50
(gdb) 
$5 = 50
(gdb) q
A debugging session is active.

	Inferior 1 [process 1582] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/local/mysql/bin/mysqld, process 1582
[Inferior 1 (process 1582) detached]
[root@node1 sysbench]#
  1. 再次登录验证。
[root@node1 ~]# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 146
Server version: 5.7.40-log MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
mysql> show variables like '%max_conn%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| max_connect_errors | 100   |
| max_connections    | 50    |
+--------------------+-------+
2 rows in set (0.10 sec)

此时,MySQL 最大连接数 max_connections 已经成功修改。并且,前面的 sysbench 压测数据还正常运行,可以理解为基本上不影响业务。

4. 总结

  1. 本次虽然只介绍了通过不重启的方式,用 gdb 修改 max_connections 参数。但是其他参数的修改也类似,可以使用同样的方式。
  2. 尽管在实验中看到了修改参数成功了,但是操作始终有未知风险的,不能总是觉得有了这些工具,一定能够万无一失。
  3. 一定要严格遵守开发规范,数据库的问题,往往是人在使用时的不当操作导致的。例如这个最大连接数,我们要评估好业务最大的并发的峰值,设置成比业务峰值大很多,就能尽量避免出现连接数打满的问题了。
  4. 另外,该参数也不是设置得越大越好,需要评估物理资源并配合压测情况得出一个合适的值,否则程序异常死锁导致不断申请新连接产生的连接数异常打满时,可能直接把数据库搞挂了。
这个错误信息: ``` Data source rejected establishment of connection, message from server: "Too many connections" ``` 是一个典型的 **数据库连接异常**,表示你的 Java 应用尝试连接数据库时,被数据库服务器拒绝,原因是: > **数据库已经达到了最大连接数限制(max_connections)** --- ## ✅ 错误原因详解 ### 🧠 1. **数据库最大连接数限制** MySQL、PostgreSQL 等数据库都有一个配置项,限制最大允许的连接数。例如: - MySQL 的默认 `max_connections` 通常是 151 或 214。 - 如果你的应用或多个应用连接数超过这个值,新的连接请求就会被拒绝。 你可以通过以下 SQL 查看当前最大连接数(以 MySQL 为例): ```sql SHOW VARIABLES LIKE 'max_connections'; ``` 查看当前已使用的连接数: ```sql SHOW STATUS LIKE 'Threads_connected'; ``` --- ### 🧠 2. **连接池配置不合理** 如果你使用的是连接池(如 HikariCP、Druid、DBCP、C3P0 等),常见问题包括: - **最大连接池数设置过高** - **连接未正确释放(未 close)导致连接泄漏** - **连接池未配置合理的超时机制(连接超时、空闲超时)** --- ### 🧠 3. **应用未正确关闭连接** 即使你使用了连接池,如果代码中没有正确关闭 `Connection`、`Statement`、`ResultSet`,会导致连接泄漏。 示例错误代码: ```java Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT ..."); // 没有关闭 rs, stmt, conn ``` 应改为: ```java try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT ...")) { // 使用 rs } catch (SQLException e) { e.printStackTrace(); } ``` --- ### 🧠 4. **数据库服务器资源不足** - 内存、CPU 不足,导致数据库无法处理更多连接。 - 操作系统文件描述符限制(Too many open files)也可能导致连接失败。 --- ## ✅ 解决方法 ### ✅ 1. **增加数据库最大连接数(临时解决)** 以 MySQL 为例,在 `my.cnf` 或 `my.ini` 中修改: ```ini [mysqld] max_connections = 1000 ``` 然后重启 MySQL 服务。 --- ### ✅ 2. **优化连接池配置** 以 HikariCP 配置为例: ```java HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb"); config.setUsername("root"); config.setPassword("password"); config.setMaximumPoolSize(20); // 设置合适的连接池大小 config.setIdleTimeout(30000); config.setMaxLifetime(1800000); config.setConnectionTimeout(30000); ``` 避免设置过大的 `maximumPoolSize`。 --- ### ✅ 3. **检查连接是否泄漏** 使用连接池时,可以开启连接泄漏检测: 以 HikariCP 为例: ```java config.setLeakDetectionThreshold(5000); // 毫秒,超过这个时间未释放连接,会打印警告 ``` 日志中会提示类似: ``` Connection leak detection triggered for ... ``` --- ### ✅ 4. **重启数据库服务** 如果当前连接数已经满了,可以临时重启数据库服务释放连接: ```bash sudo systemctl restart mysql ``` > ⚠️ 仅用于临时解决,根本问题应从连接池和代码优化入手。 --- ### ✅ 5. **优化应用逻辑** - **避免在循环中频繁创建连接** - **使用单例模式管理数据源** - **使用缓存减少数据库访问** - **异步处理数据库操作** --- ## ✅ 示例:使用 try-with-resources 正确关闭资源 ```java try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement("SELECT * FROM users"); ResultSet rs = ps.executeQuery()) { while (rs.next()) { System.out.println(rs.getString("name")); } } catch (SQLException e) { e.printStackTrace(); } ``` --- ## ✅ 总结 | 原因 | 解决方案 | |------|----------| | 数据库最大连接数限制 | 增加 `max_connections` | | 连接池配置不合理 | 限制最大连接池大小 | | 连接未关闭 | 使用 try-with-resources | | 连接泄漏 | 开启连接泄漏检测 | | 资源不足 | 优化 SQL、减少并发连接数 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值