apache中php数据库慢需分层定位:先查连接建立、查询执行或连接耗尽哪一环,再针对性优化;持久连接在prefork下风险大,event+php-fpm下基本无效;mpm与mysql连接数须对齐;应优先用proxysql等外部连接池而非pdo持久化。

Apache 中 PHP 连接数据库慢,不是换配置就能解决的问题,而是要分层定位:是连接建立慢?查询执行慢?还是连接被耗尽、复用失败?
查清到底是哪一环在拖后腿
别急着改 MaxRequestWorkers 或 max_connections。先确认瓶颈在哪一层:
- 用
mysqladmin -u root -p extended-status | grep Threads_connected看当前数据库活跃连接数是否接近max_connections上限; - 在 PHP 脚本里加日志,记录
microtime(true)时间点:连接前、new PDO()后、$pdo->query()执行后 —— 判断是“连不上”还是“查得慢”; - 检查 Apache 错误日志里有没有
PHP Warning: mysqli_connect(): (HY000/2002): Connection refused或超时类报错; - 运行
ss -s或netstat -an | grep :3306 | wc -l,看 ESTABLISHED + TIME_WAIT 是否异常高(说明连接没及时释放)。
如果连接建立就耗时 >500ms,大概率是 DNS 解析、网络延迟或 MySQL 服务响应卡顿;如果连接快但查询慢,那就该优化 SQL 和索引,而不是调 Apache。
PHP 持久连接(Persistent Connections)别乱开
很多人以为开启 mysqli.default_persistent=On 或 PDO::ATTR_PERSISTENT=true 就能提速,结果反而更慢甚至出错 —— 因为它不等于连接池,只是让 PHP 进程复用自己上次建的连接,而 Apache 的 prefork 模式下每个子进程独立,连接无法跨进程共享。
立即学习“PHP免费学习笔记(深入)”;
- 在 prefork MPM 下,持久连接实际效果有限,且容易因连接泄漏导致 MySQL
Too many connections; - 若用 event MPM + PHP-FPM,则
PDO::ATTR_PERSISTENT基本无效(FPM worker 是短命的),应直接上连接池; - MySQL 配置里
wait_timeout默认 8 小时,但 Apache 子进程可能长期存活,导致连接在 DB 侧被断开,PHP 却还拿着失效句柄,下次 query 直接报错; - 真要用持久连接,必须配合
mysqli.reconnect=On(PHP 7.1+ 默认关闭),否则断连后不会自动重连。
简单说:**prefork + 持久连接 = 风险大于收益;event + PHP-FPM + 持久连接 = 基本没用。**
Apache MPM 与数据库连接数必须对齐
Apache 并发能力上限和 MySQL 最大连接数之间得有安全余量,否则请求排队等连接,响应时间直接拉长。
-
MaxRequestWorkers(旧称MaxClients)不能超过 MySQL 的max_connections减去系统保留连接(如监控、备份脚本占用); - 比如 MySQL 设置了
max_connections = 500,那 Apache 的MaxRequestWorkers建议 ≤ 400,并留出空间给后台任务; - prefork 模式下,每个请求一个进程,每个进程可能打开多个 DB 连接(比如 ORM 多次
new PDO),所以还要看应用代码里最大并发连接数(maxTotal)是否远超MaxRequestWorkers; - 如果用了 PHP-FPM,更要核对
pm.max_children和 MySQL 连接池maxTotal,三者必须协同,不能只调 Apache。
常见错误是把 MaxRequestWorkers 设成 1000,MySQL 却只允许 200 连接 —— 结果 800 个请求全卡在“等待数据库连接”状态,KeepAliveTimeout 再小也没用。
该上连接池时就别硬扛
PHP 自身没有原生连接池,PDO::ATTR_PERSISTENT 不是解药,mysqli::real_connect() 更不是。真正可控、可监控、可回收的连接复用,得靠外部组件。
- PHP 应用推荐用
pgbouncer(PostgreSQL)或mysql-proxy/ProxySQL(MySQL),它们工作在 TCP 层,对应用透明,支持连接复用、读写分离、慢查询拦截; - 纯 PHP 实现的连接池(如
amphp/mysql)适合协程环境(Swoole、RoadRunner),但在传统 Apache + mod_php 下无法复用连接; - 如果必须用 PHP 写池子,至少确保:连接获取带
maxWaitMillis超时(避免线程挂死)、空闲连接定期SELECT 1校验、失效连接主动close()而非等 GC; - 别信“PDO 持久连接 +
unset($pdo)就能释放”的说法 —— 持久连接不会被unset销毁,只会在进程退出时关闭。
最常被忽略的一点:连接池本身也是单点,它和 MySQL 之间的连接也要设 wait_timeout,且应略小于 MySQL 的值,否则池子里的连接会静默断开,却还在往 DB 发请求。











