MySQL连接数爆满时,需分层限流:系统层用iptables限IP新建连接速率,代理层(如ProxySQL)做SQL级限流,MySQL调优wait_timeout至300–600秒并监控Threads_connected与Aborted_connects,同时配平net.core.somaxconn与back_log防队列溢出。

MySQL连接数爆满时,max_connections不是万能解药
设再高的max_connections也挡不住恶意短连接洪峰——连接刚建就断,反复重试,wait_timeout还没触发,连接池已耗尽。真正要压住的是单位时间内的新建连接速率,而非总上限。
实操建议:
- 用
iptables或firewalld在系统层限速,例如每秒只允许同一IP新建3个TCP连接:iptables -A INPUT -p tcp --dport 3306 -m connlimit --connlimit-above 3 --connlimit-mask 32 -j DROP - MySQL自身不提供连接速率限制,
max_connect_errors只防暴力密码爆破,对DDOS无效 - 若用代理(如
ProxySQL或HAProxy),必须在代理层配置rate_limit或stick-table,MySQL后端只负责处理“已放行”的连接
为什么wait_timeout和interactive_timeout调太低反而更危险
把这两个值从默认28800秒(8小时)砍到60秒,看似能更快回收空闲连接,但实际会让应用层频繁遭遇Lost connection to MySQL server during query——尤其在长事务、批量导入或网络抖动时。连接被杀后,应用重连逻辑若没做幂等或退避,会瞬间制造更多新连接,加剧雪崩。
实操建议:
- 保持
wait_timeout在300–600秒之间,足够覆盖大多数慢查询,又不至于让空闲连接长期占位 - 应用侧必须实现连接健康检查(如执行
SELECT 1)和指数退避重连,不能一断就狂连 - 监控
Threads_connected和Aborted_connects指标,如果后者突增且伴随大量Host 'x.x.x.x' is blocked because of many connection errors,说明max_connect_errors被误触发,需调高或清空host_cache
ProxySQL是目前最可行的请求级限流方案
MySQL原生不支持SQL粒度的QPS限制,max_user_connections只能按用户限并发数,无法区分正常查询和高频SELECT 1探测。ProxySQL可基于规则对特定语句、用户、来源IP做精确限流。
实操建议:
- 在
mysql_query_rules表中插入规则,例如限制某用户每秒最多执行5次SELECT:INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,servers_list,apply) VALUES (1,1,'^SELECT',1,'127.0.0.1:3306',1),再配合mysql_users中的max_connections和max_connections_per_hour - 启用
stats_mysql_query_rules视图实时观察命中率,避免规则写错导致全量请求被误限 - 注意ProxySQL自身也有连接数限制(
mysql-max_connections配置项),别让它成为新的瓶颈点
连接队列溢出:net.core.somaxconn和back_log必须配平
当瞬时连接请求超过内核TCP连接队列长度,Linux会直接丢包,客户端看到的是Connection refused;而MySQL的back_log值若小于net.core.somaxconn,MySQL无法及时从队列取连接,照样积压丢弃。
实操建议:
- 先调系统参数:
sysctl -w net.core.somaxconn=4096,并写入/etc/sysctl.conf - 再同步调MySQL的
back_log(需重启生效):值应≥net.core.somaxconn,但不超过net.core.somaxconn的1.5倍,否则浪费内存 - 验证是否生效:用
ss -lnt | grep :3306看Recv-Q是否长期非零,若持续>0,说明队列仍在堆积
真正难的不是配几个参数,而是得清楚每一层(网络栈→代理→MySQL内核→应用连接池)谁在控什么、谁在漏什么。漏掉任意一层,限流就形同虚设。










