本文约 3,135 字,预计阅读 8 分钟。文中命令与配置均经过实测,可直接参考执行。
引言
网站平时好好的,一到促销、活动或被采集,就突然报 Too many connections(连接数过多),前台直接 500,后台也进不去。这个报错的意思是:当前 MySQL 允许的最大同时连接数已经被占满,新的连接请求只能被拒绝。对 WordPress 站点来说,这往往不是流量真的有多大,而是数据库连接被”用满不还”了:比如某个插件开了持久连接、慢查询堆积占了连接、默认的 max_connections 数值太小、或者大量请求同时压到数据库。要解决它,既要临时救火让网站先活过来,也要从参数、代码、缓存几个层面治本。本文给你一套完整方案,改 MySQL 配置前记得先备份配置文件。
故障原因分析
- max_connections 参数太小。MySQL 默认值通常只有 151,而实际并发一旦超过它,就会拒绝新连接。
- 连接泄漏或持久连接。插件或主题用了持久连接(p:localhost)且没有正确释放,连接只增不减;或者代码报错导致连接没被归还。
- 慢查询长期占用连接。一条没走索引的大查询可能跑几十秒,期间连接一直被占着,并发一高,连接池很快耗尽。
- 突发流量压垮数据库。秒杀、被采集、爬虫集中抓取时,大量并发请求同时建立连接。
- 缺少缓存层。每个页面请求都直接查数据库,没有页面缓存或对象缓存,数据库压力被无限放大。
- 应用程序连接池设置不当。PHP-FPM 子进程数、进程超时设置过大,导致每个请求都长期持有一条数据库连接。
分步解决方案
第一步:先看当前连接状态
登录 MySQL 或用 phpMyAdmin 执行:
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
SHOW VARIABLES LIKE 'max_connections';
Threads_connected是当前连接数;Max_used_connections是历史峰值;max_connections是上限。
如果 Max_used_connections 长期贴近 max_connections,说明就该调大上限或优化查询了。
第二步:临时救火,杀掉空闲连接
网站已经打不开、又连不上 MySQL 时,如果还能用 root 通过本地 socket 登录(这时会保留一个 SUPER 用户通道),先看是谁在占连接:
SHOW PROCESSLIST;
关注 Command 为 Sleep、Time 很大的连接,以及执行时间长的 Query。杀掉空闲连接释放名额:
SELECT GROUP_CONCAT(id)
FROM information_schema.PROCESSLIST
WHERE command = 'Sleep' AND time > 300;
拿到 id 列表后逐个:
KILL 12345;
也可以找出空闲超过一定时间的连接,批量生成 KILL 语句,再复制执行。注意:切勿 KILL 正在执行写操作的关键连接,否则可能中断事务。
第三步:调大 max_connections(治标)
修改 MySQL 配置文件前先备份,Linux 上通常是 /etc/my.cnf 或 /etc/mysql/my.cnf。在 [mysqld] 段加入:
max_connections = 500
保存后重启 MySQL 生效。如果没法重启,可以动态调整(重启后失效):
SET GLOBAL max_connections = 500;
但调大不是越大越好:每个连接都要占用内存(尤其配合较大的 buffer),设置过高会拖垮内存。建议结合服务器内存,按经验值,每 4GB 内存可支撑约 200~300 连接,再留出余量,逐步往上调并观察负载。
第四步:排查并优化慢查询(治本)
调大连接数只是把上限抬高,真正占连接的往往是慢查询。开启慢查询日志:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
等运行一段后查看慢查询日志,找出耗时最长的 SQL,重点检查是否缺索引。对 WordPress 来说,最常见的慢查询来自:
SELECT * FROM wp_postmeta WHERE meta_key = '某个键';
如果 wp_postmeta 的 meta_key 没索引、又数据量巨大,这类查询会特别慢。可以添加索引(操作前备份,并评估写入影响):
ALTER TABLE wp_postmeta ADD INDEX meta_key (meta_key(191));
ALTER TABLE wp_options ADD INDEX option_name (option_name(191));
第五步:为 WordPress 加缓存,从根本上降低数据库并发
这是最有效的一招。安装页面缓存插件(如 WP Super Cache、W3 Total Cache、LiteSpeed Cache),让绝大多数访问直接返回静态页面,不再连数据库。再配合对象缓存(Redis 或 Memcached),把常用的查询结果缓存起来。这样即使并发翻几倍,落到 MySQL 的连接也会大幅减少。
同时检查是否有插件使用了持久连接,可在 wp-config.php 里排查并关闭。必要时降低 PHP-FPM 的 pm.max_children,避免进程数超过数据库能承受的连接上限。
第六步:调整连接超时参数
缩短空闲连接的等待时间,让连接快点释放:
SET GLOBAL wait_timeout = 60;
SET GLOBAL interactive_timeout = 60;
把这两个值调小(比如 60~120 秒),Sleep 状态的连接会更快被回收,可有效缓解连接堆积,但要确保大于 PHP 的最长执行时间,避免正常连接被误杀。
第七步:验证
改完后重启 MySQL,用 SHOW STATUS LIKE 'Threads_connected'; 观察连接数是否平稳,结合压力测试或真实流量,确认 Too many connections 不再出现、网站响应正常。
常见踩坑点
- 把 max_connections 调到特别大。内存跟不上会导致 MySQL 崩溃或系统 swap,得不偿失。
- 只调参数不查慢查询。连接数上去了,问题被掩盖,跑一阵还会复发。
- 乱 KILL 连接。可能中断正在写的数据,导致数据不一致。
- 持久连接没处理好。连接只借不还,是连接泄漏的常见来源。
- 改了配置文件不重启。静态参数必须重启才生效,动态 SET GLOBAL 重启即失效。
- 忽略 PHP-FPM 与连接数关系。PHP 进程开太多,会瞬间建立远超数据库承受能力的连接,需要一起调。
总结
MySQL 连接数过多报 Too many connections,直接原因是 max_connections 被占满,但根子往往在慢查询、连接泄漏、缺少缓存这三点。处理时先临时救火(查看进程、KILL 空闲连接),再调大 max_connections 和缩短 wait_timeout 治标,最后通过加页面缓存与对象缓存、优化慢查询、给 wp_postmeta 等表补索引来治本。对 WordPress 站长来说,上了缓存插件后,数据库连接压力通常能下降一个数量级。调整 MySQL 配置前请备份配置文件,参数改动循序渐进,观察几天再决定是否继续上调。
