本文目录导读:

在PHP项目中定位慢日志和性能瓶颈,可以按照从宏观到微观的顺序进行排查。
下面是一套完整的排查流程、常用工具和命令。
第一步:开启与查看慢日志
这是最直接的第一步,它可以告诉你哪些请求慢以及卡在哪个环节。
PHP-FPM 慢日志(重点排查)
如果你的项目使用 Nginx + PHP-FPM,这是最常用的方式。
- 开启配置:在
php-fpm.conf或/etc/php/8.x/fpm/pool.d/www.conf中。 - 关键参数:
request_slowlog_timeout = 5s(执行超过5秒的请求记录)slowlog = /var/log/php-fpm-slow.log(日志路径)
- 使用方法:修改后重启 FPM:
sudo systemctl reload php-fpm
- 日志分析(重点看堆栈):慢日志不仅告诉你哪个URL慢,还会打印出函数调用堆栈,这是定位瓶颈的关键。
[22-Jun-2023 10:00:00] [pool www] pid 1234 script_filename = /var/www/html/public/index.php [0x00007f...] () /var/www/html/vendor/symfony/... 第100行 [0x00007f...] SomeFunction() /var/www/html/app/... 第50行 [0x00007f...] DatabaseQuery() /var/www/html/app/... 第20行如果看到堆栈停留在
curl_exec或file_get_contents,说明是外部HTTP请求慢;如果停留在mysqli_query或PDO->query,说明是数据库慢。
Nginx 访问日志(辅助)
用于统计哪些URI平均响应时间最长。
- 修改
nginx.conf中的log_format,加入请求耗时$request_time和上游耗时$upstream_response_time。log_format main '$remote_addr - $request_time - $upstream_response_time - "$request"';
- 分析:
awk '{print $7, $10}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
第二步:定位代码瓶颈(Profile分析)
慢日志只能告诉你大致的堆栈,但要找具体的函数耗时,需要使用 Profiler。
推荐工具:Xdebug + Qcachegrind
- 安装:
pecl install xdebug
- 配置(
php.ini):xdebug.mode=profile xdebug.output_dir=/tmp/xdebug xdebug.start_with_request=trigger
- 使用:访问
http://your-site.com/?XDEBUG_PROFILE,会在/tmp/xdebug生成文件。 - 分析:用
Qcachegrind(Linux/Mac)或WinCacheGrind(Windows)打开文件,你会看到瀑布图,一眼就能看出哪个函数独占时间最长(通常是self时间高)。
轻量级方案:Tideways / OneAPM / 阿里云ARMS
如果不想折腾,建议直接使用SaaS服务,它们自动标记慢事务、SQL慢查询、外部调用延迟,开箱即用。
第三步:最常见的三大瓶颈(深入分析)
大多数慢请求逃不出这3个原因,请重点排查:
数据库慢查询(占比最高)
- 开启 MySQL 慢日志:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 超过1秒记录 SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
- 分析工具:
mysqldumpslow -s at /var/log/mysql/mysql-slow.log - 重点检查:
- 是否命中索引:用
EXPLAIN SELECT ...查看type是否为const/ref,rows是否很大。 - N+1查询问题(ORM最常见) :如果使用了 Laravel 或 ThinkPHP,查看是否有循环里查询数据库,如果有,使用
with()预加载。
- 是否命中索引:用
外部API调用(Redis / HTTP / 第三方接口)
- 排查方法:在代码中记录每次调用时间,或者利用 Xdebug Profile 查看时间消耗在
curl_exec上。 - 解决方案:
- 加超时:
curl_setopt($ch, CURLOPT_TIMEOUT, 3),如果第三方服务挂了,你的进程会卡住直到超时,设置短超时至关重要。 - 并发变串行:如果你调用了3个互相独立的API,总共耗时就是3个时间相加,改成并发请求(
curl_multi_exec或 Swoole)可大幅提升速度。
- 加超时:
内存耗尽 / 垃圾回收(GC)问题
- 排查:如果系统负载不高,但进程GC频繁,检查是否有大数组导致内存占用高。
- 命令:在业务代码里临时插入:
file_put_contents('/tmp/memory.txt', memory_get_peak_usage(true));
第四步:环境级瓶颈(基础设施)
如果代码优化后仍慢,检查以下环境指标:
- CPU与IO:使用
top或htop查看%wa(IO等待),如果IO等待高,多半是磁盘读写慢(例如日志写入或文件缓存)。 - Redis 命中率:如果用了 Redis 做缓存,但命中率很低,导致大量请求穿透到数据库,使用
redis-cli info stats查看keyspace_hits和keyspace_misses。 - PHP OpCache:
- 确认已开启:
php -i | grep opcache.enable - 如果没开启,PHP每次都需要解析编译代码,CPU开销极大。
- 确认已开启:
第五步:现代PHP异步化(进阶)
如果使用传统 FPM(每个请求一个进程,慢请求会占满进程导致队列堵塞):
- 痛点:某个接口很慢(如导出Excel),会占满所有PHP-FPM进程,导致其他请求排队。
- 解决方案:如果业务合适,可以引入 Swoole 或 Workerman,将常驻内存与协程结合,瓶颈定位方式也会从“加机器”变为“看协程调度”。
定位排查命令速查表
| 问题描述 | 执行命令 | 期望结果 |
|---|---|---|
| 请求超时 | tail -f /var/log/php-fpm-slow.log |
看到堆栈,确定是DB还是API |
| 单条SQL慢 | tail -f /var/log/mysql/mysql-slow.log |
找到慢SQL,执行 EXPLAIN |
| 平均响应慢 | ab -n 1000 -c 50 http://test.com/login |
查看平均时间,对比优化前后 |
| CPU 100% | top -Hp <php-fpm-pid> |
确认是CPU密集(算法)还是IO密集(等待) |
| 内存溢出 | grep 'Allowed memory' error.log |
增加 memory_limit 或优化代码 |
建议的排查顺序永远是: 网络/负载 → PHP-FPM慢日志 → 数据库慢日志 → Xdebug Profile 代码剖析 → 优化外部API/IO。 遵循这个顺序,基本能定位90%的PHP项目性能问题。