ab压测QPS低但CPU不高,说明瓶颈在ab自身或服务端排队而非PHP代码;ab单线程阻塞、不支持HTTP/2、keep-alive不智能,易被Nginx/FPM队列卡住,导致Requests per second卡在200–400、Time per request飙升而PHP-FPM CPU仅10%–30%。

ab压测结果里QPS低但CPU不高,说明什么
这通常不是PHP代码慢,而是ab自身瓶颈或服务端排队导致的假象。ab是单线程阻塞模型,-c 100 并发实际靠串行复用连接,无法打满现代PHP-FPM的并发能力;同时它不支持HTTP/2、keep-alive复用不够智能,容易被Nginx或FPM的连接队列卡住。
常见表现:Requests per second卡在 200–400 左右,Time per request飙升,但 top 看 PHP-FPM 进程 CPU 却只有 10%–30%。
- 先确认是不是ab拖后腿:换
wrk -t4 -c400 -d10s http://...对比,如果QPS翻倍以上,基本坐实ab限制 - 检查FPM是否真在排队:访问
pm.status_path(如/status?full),看listen queue值是否持续 > 0 - 别信ab的
Failed requests:它把超时、重定向、SSL握手失败全算作失败,不区分服务端错误和客户端网络问题
wrk压出高延迟却看不到PHP慢日志记录
说明耗时没花在PHP执行阶段,而是在FPM调度或上游等待环节。wrk默认启用keep-alive和多连接,会真实触发FPM的进程争抢——如果 pm.max_children = 50,但wrk开了 -c 400,那350个请求就在 pm.queue_length 里干等,slowlog 根本不会触发(它只录“PHP执行超时”,不录“排队超时”)。
关键判断依据:curl -w '%{time_starttransfer} %{time_total}\n' -o /dev/null -s URL 中 time_starttransfer 明显大于 100ms,但FPM slowlog无记录 → 排队或Web服务器层延迟。
立即学习“PHP免费学习笔记(深入)”;
- 查Nginx access log里的
$upstream_response_time和$request_time差值,差值大说明请求卡在upstream队列 - 用
pm.status_path观察max children reached是否频繁增长 - 临时调大
pm.max_children再压一次,如果延迟骤降,就不用再往下查PHP代码了
为什么wrk的Lua脚本里不能直接用file_get_contents()
因为wrk运行在C层,LuaJIT沙箱里没有PHP运行环境,所有PHP函数都不可见。file_get_contents() 是PHP扩展函数,wrk的Lua只能调用它内置的 http.request() 或系统命令(如 os.execute("curl ..."),但极不推荐)。
你真正需要的是用Lua构造带动态参数的请求,比如登录态续期、时间戳注入、用户ID轮询——这些必须靠 wrk.method = "POST" + wrk.body 拼接,而不是幻想在Lua里跑PHP逻辑。
- 正确做法:在
init阶段预生成token列表,request阶段按协程ID取用 - 错误做法:试图在
request函数里os.execute("php -r 'echo time();'")——开销爆炸且不可控 - 注意:wrk的
script.lua每个线程只执行一次init,但每次请求都会走request,别在后者里做重操作
压测时FPM slowlog有记录,但Blackfire火焰图里没看到对应函数
说明slowlog捕获的是“从FPM handing over到PHP脚本结束”的总时间,而Blackfire只分析PHP代码执行段。两者时间范围不同:slowlog包含OPcache加载、自动加载器(autoload)解析、扩展初始化(如PDO连接池建立)、甚至某些扩展的ZTS锁等待——这些Blackfire默认不埋点。
典型例子:slowlog显示某请求耗时 800ms,Blackfire只显示 120ms 的PHP执行,剩下近700ms大概率花在 autoload 查找类文件、或 new PDO() 建连(尤其当MySQL连接池未启用时)。
- 先看slowlog里有没有
[pool www] child 12345 said into stdout: "..."这类扩展输出,有就说明卡在扩展层 - 用
strace -p $(pgrep php-fpm) -e trace=open,connect,sendto抓系统调用,确认是否卡在文件IO或网络connect - Blackfire要开全量采集:加
blackfire --config=... index.php而非仅浏览器插件,否则漏掉CLI模式下的autoload行为



















