“Too many connections”是MySQL服务端主动拒绝新连接,因max_connections耗尽,即使账号密码正确、网络畅通也立即断开握手,不排队不等待。

为什么 Too many connections 不是“连不上”,而是“被拒绝”
这个错误本质是 MySQL 服务端主动拒接新连接,不是客户端网络问题或密码错。只要 max_connections 被耗尽,哪怕账号密码完全正确、网络畅通,也会立刻返回该错误——它不排队、不等待,直接断掉握手。
常见诱因不是突发流量,而是连接没释放:比如 PHP-FPM 进程复用连接但未调用 mysqli_close() 或 PDO 的 null 引用;又或者 Python 的 mysql-connector-python 在异常路径里漏了 connection.close()。
- 检查当前活跃连接数:
SHOW STATUS LIKE 'Threads_connected'; - 确认上限值:
SHOW VARIABLES LIKE 'max_connections'; - 别只看“用了多少”,重点看“谁占着不放”:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE TIME > 60;(查持续超 60 秒的空闲连接)
临时救急:快速释放无效连接 + 安全调高上限
线上报错时,先止损再根治。不能直接 kill 所有连接,得区分“真业务连接”和“僵死连接”。
- 优先 kill 掉
Command = 'Sleep'且Time > 300(5 分钟)的连接:KILL [ID];(逐个执行,别用批量脚本误杀) - 临时调高上限(仅限应急):
SET GLOBAL max_connections = 512;(注意:该值重启失效,且过高可能压垮内存) - 如果连
root都连不上,说明连管理员连接槽也被占满。此时只能登录服务器,用mysqladmin -u root -p shutdown重启 mysqld(慎用,有事务丢失风险)
PHP 持久连接(pdo_mysql / mysqli)最容易踩的坑
开启持久连接本意是省开销,但若应用没做连接池管理,反而会把连接钉在进程里不释放,最终撑爆 max_connections。
-
PDO::ATTR_PERSISTENT => true是开关,但它不自动回收;Apache prefork 模式下每个子进程持有一个持久连接,子进程不死,连接就不归还 - 不要在长生命周期脚本(如 Laravel Queue Worker)里用持久连接,它们常驻内存,连接越积越多
- 更稳妥的做法:关掉持久连接,改用连接池中间件(如 ProxySQL)或应用层短连接 + 连接复用逻辑(如 Laravel 的
DB::reconnect())
连接数监控和上线前必须做的三件事
这个错误往往在低峰期不暴露,一到大促或定时任务集中跑就崩。靠“出事再查”永远被动。
- 在监控系统里加两个硬指标告警:
Threads_connected / max_connections > 0.8和Aborted_connects突增(说明连接建立失败率升高) - 上线新服务前,用
sysbench --threads=200 --time=300 oltp_read_write压测连接行为,观察Threads_connected是否随并发线性增长后回落 - 所有数据库访问封装层(DAO / Repository)必须确保:无论正常流程还是
try/catch异常分支,都调用close()或让连接对象超出作用域(Python/Go 依赖 GC,PHP 则必须显式 close)
真正难的不是调参数,是让每个连接都有明确的“出生”和“死亡”时机。很多团队花几小时调 max_connections,却从没看过自己代码里 $pdo 变量到底在哪一行被销毁。










