根本原因是ci3的pconnect复用连接但不健康检查、不自动回收,叠加mysql wait_timeout与php-fpm生命周期错配导致连接堆积;应调低wait_timeout、限制pm.max_requests、应用层探测连接并重连,或改用短连接+代理方案。

CI3(CodeIgniter 3)中将 pconnect 设为 TRUE 后频繁报 Too many connections,根本原因不是“用了长连接”,而是长连接被复用但未被正确管理——它让连接在 PHP-FPM 进程生命周期内持续持有,而 CI3 的数据库类本身不自动回收、不检测失效、不驱逐空闲连接,叠加 MySQL 的 wait_timeout 和应用层超时配置错位,最终导致连接堆积。
为什么 pconnect=TRUE 反而更容易连满
CI3 的 pconnect 基于 PHP 的 mysql_pconnect() 或 mysqli_pconnect(),其机制是:同一 PHP-FPM worker 进程内,后续请求会复用已建立的连接,而不是新建。问题在于:
- CI3 不做连接健康检查——连接若被 MySQL 主动断开(如 wait_timeout 到期),下次调用仍会尝试复用,失败后不自动重建,也不释放旧句柄
- PHP-FPM worker 长时间存活(尤其配置了
pm.max_requests = 0或值很大),连接就一直挂在那儿,哪怕业务早已不用 - 每个 worker 绑定一个连接,若
pm.max_children = 50,且所有 worker 都连上了,MySQL 至少要预留 50+ 连接,再叠加后台任务、命令行脚本等,极易触达max_connections - CI3 没有连接池的 idle 驱逐逻辑,不会主动清理长时间空闲的 persistent 连接
关键错配:wait_timeout vs PHP-FPM 生命周期
MySQL 默认 wait_timeout = 28800(8 小时),但 CI3 + pconnect 场景下,真正决定连接是否“活着”的,其实是PHP worker 的存活时长。常见风险组合:
- PHP-FPM 设置
pm.max_requests = 0(永不重启 worker)+ MySQLwait_timeout = 28800→ 连接可挂 8 小时,但实际业务可能只每分钟来一次请求,大量连接长期 Sleep 占坑 - MySQL
wait_timeout = 300(5 分钟),但 PHP worker 在 5 分钟内没被复用,连接被 MySQL 断开;下次请求复用时,CI3 报MySQL server has gone away,却不释放该句柄,继续占着连接槽位 -
interactive_timeout未同步调整,导致某些 CLI 脚本或监控连接超时更长,进一步挤占名额
CI3 场景下的实操修复点
不推荐直接关 pconnect(影响性能),应聚焦“让长连接真正可控”:
- 在
/etc/my.cnf的[mysqld]段统一设:
wait_timeout = 300
interactive_timeout = 300
然后重启 MySQL —— 强制空闲连接 5 分钟断开,避免无限悬挂 - 限制 PHP-FPM worker 寿命:在
www.conf中设pm.max_requests = 1000(建议 500–2000),让 worker 定期重启,自然释放 persistent 连接 - CI3 应用层加兜底:在每次数据库操作前,手动探测连接有效性(例如执行
SELECT 1),失败则强制$this->db->reconnect();或封装 DB 类,在__destruct中尝试mysqli_close()(对 persistent 连接无效,但可减少误判) - 禁用不必要的 CLI 脚本持久连接:在定时任务或命令行中显式设
$db['default']['pconnect'] = FALSE;,避免 cron 占用连接不放
比改配置更有效的替代方案
CI3 原生不支持现代连接池语义,长期来看,更稳妥的做法是:
- 用 Nginx + PHP-FPM 的
pm.max_children值反推合理max_connections:比如 FPM 最多 30 个子进程,MySQLmax_connections设为 60~80 即可,留出余量给管理员和复制线程 - 将高频、低延迟接口迁至 PDO + 短连接(
pconnect=FALSE),配合 PHP OPcache 和查询缓存,性能损失有限,但连接行为完全可控 - 在架构层引入代理(如 ProxySQL 或 MaxScale),做连接复用和熔断,把连接管理从应用下沉到中间件











