根本原因是TP5.1默认短连接+未复用+PHP-FPM与MySQL参数不匹配导致连接堆积;需从PDO长连接配置、FPM进程数精准计算(pm.max_children)、MySQL的wait_timeout等参数协同优化,并通过SHOW STATUS和FPM状态页验证效果。

TP5.1在Nginx+PHP-FPM环境下数据库连接数飙升,根本原因不是并发高本身,而是每次请求都新建PDO连接、未复用、未释放,加上FPM进程模型和MySQL服务端参数不匹配,导致连接堆积甚至耗尽max_connections。优化要从PHP层连接复用、FPM资源调度、MySQL服务端协同三方面入手,而不是单纯调大max_connections。
强制启用PDO长连接并显式管理
ThinkPHP 5.1默认使用短连接,Db::name()每调用一次都可能触发新PDO实例创建,尤其在循环或嵌套查询中极易失控。必须主动开启长连接并确保配置生效:
- 在
config/database.php中,为对应数据库配置添加'params' => [\PDO::ATTR_PERSISTENT => true] - 确认
php.ini中mysql.allow_persistent = On且mysqli.reconnect = On已启用 - 避免在控制器/模型中反复调用
Db::connect();统一使用Db::name('user')->where(...)->find()走同一连接池路径 - 若使用多库,不同库配置需指定不同
dsn或host:port,防止连接复用冲突
调整PHP-FPM进程模型与数量
FPM进程数直接决定最大并发连接上限。设得过大,MySQL连接数被撑爆;设得太小,请求排队等待,反而触发更多重试连接。关键靠内存算准pm.max_children:
- 用
ps aux --sort=-%mem | grep php-fpm查典型进程RSS(如平均32MB) - 用
free -h看可用内存,预留20%给MySQL/Redis等,剩余按公式计算:pm.max_children ≈ (可用内存 × 0.8) ÷ 单进程RSS - 选
pm = dynamic而非ondemand:设pm.start_servers = max_children × 0.2,pm.min_spare_servers = start_servers × 0.7,pm.max_spare_servers = max_children × 0.8 - 加
pm.max_requests = 500防内存泄漏导致连接句柄未释放
同步调优MySQL服务端参数
光改PHP没用——如果MySQLwait_timeout太小(默认8小时),而PHP长连接空闲超时早于它,就会出现“连接还活着但MySQL已关”的假死状态,后续请求被迫新建连接:
立即学习“PHP免费学习笔记(深入)”;
- 修改
/etc/mysql/my.cnf,确保以下三项合理: -
max_connections = 300(建议不低于FPMmax_children的1.2倍) -
wait_timeout = 300(5分钟,略大于FPMpm.max_requests处理周期) -
interactive_timeout = 300(保持一致) - 重启MySQL:
systemctl restart mysql
补充:监控与验证是否真生效
不验证等于白调。上线后立刻检查:
- 实时查MySQL当前连接:
SHOW STATUS LIKE 'Threads_connected';,观察是否稳定在pm.max_children附近波动,而非持续攀升 - 查FPM状态页:
curl http://your-domain.com/status?full,关注active processes和listen queue len,后者长期>0说明仍存在排队 - 压测对比:用
ab -n 5000 -c 200跑同一接口,对比优化前后Threads_connected峰值和响应时间



















