Connection refused 或连接超时但无“Too many connections”提示,说明连接请求未达MySQL层,卡在TCP握手阶段;根本原因是back_log队列溢出或系统文件描述符不足,需分别验证MySQL back_log配置、net.core.somaxconn内核参数及ulimit -n限制。

客户端报 Connection refused 或连接超时,但没看到 Too many connections
这说明连接请求根本没进 MySQL,卡在了操作系统 TCP 层。常见现象是:应用启动瞬间大量建连失败、健康检查批量超时、telnet mysql-host 3306 偶尔不通。此时 SHOW STATUS LIKE 'Threads_connected'; 可能远低于 max_connections,而错误日志里也找不到 “Too many connections” —— 因为 MySQL 连线都没机会处理。
根本原因通常是两个底层限制被击穿:
-
back_log队列溢出:MySQL 的 listen() 队列满了,内核直接丢弃新 SYN 包 - 系统文件描述符(file descriptor)不足:每个 TCP 连接占用一个 fd,
ulimit -n过低会导致 accept() 失败
两者常同时出现,但排查要分开验证。
如何确认是 back_log 不足导致的连接堆积
先查 MySQL 当前 back_log 值和系统级限制是否匹配:
- 执行
SHOW VARIABLES LIKE 'back_log';,比如返回back_log = 50 - 查系统当前
net.core.somaxconn:sysctl net.core.somaxconn,若为128,则 MySQL 实际生效值会被截断为 128(即使你设了 500) - 再看
/proc/sys/net/ipv4/tcp_max_syn_backlog,它影响 SYN 队列长度,但现代内核下主要受somaxconn约束
如果 back_log 明显小于并发建连峰值(如秒杀场景瞬时 1000+ 连接请求),且 somaxconn 没同步调大,就极可能是它卡住的。典型日志线索是错误日志中反复出现 Aborted connection + unauthenticated user,且对应 IP 的连接状态停留在 Connect 阶段。
为什么调大 back_log 后还是没用
因为 back_log 不是独立参数,它依赖三个条件同时满足:
- MySQL 配置中
back_log = N(写入 my.cnf 并重启,SET GLOBAL 无效) - 系统
net.core.somaxconn ≥ N,需用sysctl -w net.core.somaxconn=2048并写入/etc/sysctl.conf - MySQL 启动时没有警告 “back_log setting reduced”,否则说明被系统截断了
漏掉任意一项,MySQL 实际使用的仍是旧值。另外注意:Linux 默认 ulimit -n 通常为 1024,而每个连接至少占 1 个 fd,若 max_connections 设到 2000,但 ulimit -n 没调高,MySQL 启动时会报错或静默降级 —— 这类问题容易被忽略,因为它不报错,只是连接数上不去。
连接句柄耗尽的快速定位方法
不是所有“连接不上”都跟 back_log 有关,得先排除句柄瓶颈:
- 查 MySQL 进程当前打开的 fd 数:
ls /proc/$(pgrep mysqld)/fd | wc -l - 对比系统限制:
cat /proc/$(pgrep mysqld)/limits | grep "Max open files" - 如果前者接近后者上限(比如 1023/1024),且
SHOW STATUS LIKE 'Open_files';也逼近open_files_limit,基本可判定是句柄不够
修复路径固定:改 /etc/security/limits.conf(对 mysql 用户加 nofile),改 systemd service 文件里的 LimitNOFILE,再重启 mysqld。不重启只改 sysctl,back_log 再大也没用——连接连 accept() 都进不来。
back_log 和句柄限制都是“看不见的墙”,它们不出错,只沉默拒绝。真正难的不是调参,而是意识到问题不在 SQL 或连接池,而在 TCP 握手前那几十毫秒里发生了什么。


















