先把当时的情况摆出来。Ubuntu 20.04 上跑 PHP 7.4,pool 里配了 request_terminate_timeout = 30s,error.log 每分钟冒出 WARNING: child 29113, script ‘/www/api/public/index.php’ execution timed out (30.02 sec),可这条接口在测试环境跑不到一秒。

原因不止一个,得分开看。接口里循环调第三方 SDK,超时和重试都写在库里,一次拉两百条就是两百次串行请求。另一种情况是查询条件用了没有索引的字段,单次四秒,放在循环里被放大成三十秒。

还有一种情况,opcache 命中率偏低,每次请求都重新编译上千个文件,时间花在编译而不是执行。另外,session 文件锁没有提前释放,同一个用户的并发请求全排在锁上。

第一件事是看报错里是否只固定出现在某个脚本

grep 'execution timed out' /var/log/php-fpm/error.log | awk '{print $8}' | sort | uniq -c

然后打开慢请求栈,让 FPM 把现场打下来

printf 'request_slowlog_timeout = 10s\nslowlog = /var/log/php-fpm/slow.log\n' >> /etc/php/7.4/fpm/pool.d/www.conf && systemctl reload php7.4-fpm

接着读慢日志栈顶,看时间停在哪个函数

tail -60 /var/log/php-fpm/slow.log

剩下的就是按总耗时排序,确认 SQL 是不是主因

mysql -uroot -p -e 'select digest_text,sum_timer_wait from performance_schema.events_statements_summary_by_digest order by sum_timer_wait desc limit 5\G'

怎么算真的好了,看的是同一批现象。同一接口连压一百次,slow.log 不再新增记录,error.log 里没有新的 execution timed out,p99 从三十秒落到一秒以内。

容易忽略的是,把 request_terminate_timeout 调大只是把失败往后推,nginx 的 fastcgi_read_timeout 要跟着放,否则前端先断开。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。

作者 9823189