手把手排查 PHP 请求变慢的隐藏系统瓶颈
PHP请求变慢时应先排查系统负载、I/O等待、系统调用、opcache与realpath_cache、swap及swappiness等底层瓶颈,结合top、vmstat、strace、dmesg、free等工具定位,并通过调整NFS、Redis连接、缓存参数和swappiness等方式解决。
应用突然变慢,第一反应往往是查数据库慢查询、看业务代码逻辑。但有时候,代码层面翻了个底朝天,数据库响应也飞快,请求却依然卡成PPT。这时候,真正的瓶颈往往藏在你看不见的系统底层。
这篇文章,我想分享几个实战中容易踩坑的“隐藏系统瓶颈”,希望能帮你拓宽排查思路。
先别急着改代码,看一眼系统负载
当你觉得 PHP 变慢了,第一步不是打开 IDE,而是先登服务器执行 top 或 uptime。观察 load average 是否已经超过了 CPU 核心数。
但这里有个误区:负载高不一定是 CPU 忙,还有可能是进程在 D 状态(不可中断睡眠),也就是在等 I/O。比如磁盘坏了、NFS 挂载卡住了,都会让进程卡在内核态,表现为 CPU 空闲但负载极高。
一次线上事故,PHP 进程全部阻塞,top 显示 wa(I/O wait)高达 40%。查了很久发现是某个日志目录挂载的 NFS 服务端重启,导致 PHP 写日志时全部卡住。遇到这种情况,dmesg -T 看看有没有 I/O 报错,或者 vmstat 1 看 b 列和 wa 列,能帮你快速锁定问题。
系统调用层面的“隐形杀手”
另一个容易被忽略的场景是:PHP 代码逻辑正常,但某次请求内部发生了大量的系统调用,比如频繁地打开文件、获取系统时间、执行外部命令等。
你可以用 strace -p 附加到一个慢请求的 PHP 进程上,看看它卡在哪个 syscall。有一次排查一个接口总是慢 3 秒,strace 显示进程卡在 connect() 上,目标地址是 127.0.0.1:6379。Redis 明明没宕机,但 redis.conf 里没设置 timeout,连接池耗尽导致新建连接的进程全部排队。这种问题用 netstat -anp | grep php 也能看出端倪——大量进程处于 SYN_SENT 状态。
opcache 与 realpath_cache 的微妙影响
这不算特别底层,但在高并发下确实容易成为隐形瓶颈。
- opcache 内存不足:如果
opcache.memory_consumption太小,而代码量很大,会导致频繁的缓存溢出和重新编译。查看opcache_get_status(),如果misses和opcache_hits比例失调,说明缓存命中率低,PHP 每次请求都要重新解析源码文件,消耗大量 CPU。 - realpath_cache_size 过小:如果你的项目用了大量
require/include且文件路径很深,默认的 realpath 缓存不够用时,PHP 每次都要去磁盘做stat()系统调用。高并发下磁盘 I/O 会显著上升。适当调大realpath_cache_size = 4096K(PHP 5.3+ 支持),往往立竿见影。
swap 分区:被忽视的“假死”元凶
Linux 下如果开启了 swap,并且 swappiness 设置过高,内存中的 PHP 进程代码段和数据可能被换到磁盘上。当请求进来时,CPU 需要把页面从 swap 换回物理内存,这个过程是磁盘 I/O,极慢。
排查方法:free -h 看 si/so 是否非零,或者用 sar -S 看 swap 使用率。给 PHP-FPM 设置合适的 pm.max_requests,让进程定期退出、释放内存,同时将 vm.swappiness 调低(比如 10),能有效减少这种情况。
隐藏的文件系统锁
最后提一个非常隐蔽的坑:NFS 锁或文件锁导致的阻塞。
假如你的 PHP 代码里用了 flock 做并发控制,或者 .env 文件是通过某云厂商的网络盘挂载的。一旦持有锁的进程异常退出,锁未释放,其他 PHP 进程会一直阻塞在flock()或file_get_contents() 上。这种问题从应用日志上完全看不出来,只有 strace 才能看到卡在 flock(fd, LOCK_SH)。
排查技巧:lsof +D /path/to/project 查看是否有进程占住了文件,或者干脆把写操作密集的文件改成本地盘。
总结一下排查思路
当代码和 SQL 都查不出问题时,不妨退一步:
uptime+vmstat区分是 CPU 还是 I/O 负载。dmesg -T+iostat -x 1排查磁盘与网络文件系统。strace -cp看系统调用耗时,定位到具体的内核阻塞点。- 检查 opcache 和 realpath cache 统计,调优后观察是否有大量无效
syscall。 - 最后再看一眼 swap 和内存策略。
PHP 的慢,从来不只是 PHP 的锅。底层系统里的每一个环节,都可能成为压垮性能的最后一根稻草。希望这篇手把手的思路,能帮你少踩几个坑。
转载请注明出处,版权归原作者所有。
正式会员
认证极客






