本文目录导读:

在 PHP 项目中,定位磁盘 IO 过高的进程(特别是 PHP-FPM 或 CLI 进程)需要从系统级别和进程级别入手,以下是分步排查方法:
第一步:确认是哪个磁盘分区和哪个进程
-
使用
iostat查看磁盘整体负载(需要sysstat包)iostat -x 1 5
- 关注
%util(磁盘利用率,接近 100% 说明繁忙)、await(IO 等待时间)、r/s、w/s(读写次数)。 - 如果特定分区(如
/dev/sda或/dev/vda)很高,则进一步定位。
- 关注
-
使用
iotop实时查看进程 IO(推荐,需要 root)iotop -oP
-o:只显示有 IO 的进程。-P:只显示进程(不显示线程)。- 按
P键按 IO 读写量排序,按p键切换进程/线程模式。 - 重点看:
DISK READ和DISK WRITE数值高的进程。
-
使用
pidstat针对特定进程查看(需要sysstat包)# 先找到可疑的高 CPU/IO 进程 PID pidstat -d 1 5
- 输出包括
kB_rd/s、kB_wr/s等。
- 输出包括
第二步:定位 PHP 进程内部行为
如果确认是 PHP 进程(如 php-fpm 或 php)导致的 IO 高,需要深入 PHP 代码层面:
-
查看 PHP 进程打开的句柄(文件描述符)
# 假设 PHP 进程 PID 是 12345 ls -l /proc/12345/fd/ | grep -E "(日志|缓存|session|暂存文件路径)" | head -20 lsof -p 12345 | grep -E "REG|DIR" | sort -k 8 | head -20
- 常见高 IO 来源:
/tmp/sess_*(PHP Session 读写频繁,特别是文件存储模式)/var/log/(日志写入过于密集,如access.log、error.log)- 文件缓存目录(如 Smarty、Twig 或 Laravel 视图缓存)
- 图片上传、文件批量处理
- 常见高 IO 来源:
-
开启 PHP 的 strace 跟踪(临时,谨慎)
# 跟踪文件写入系统调用 strace -p 12345 -e trace=write -c -S time 2>&1 | head -20 # 或者跟踪文件打开创建 strace -p 12345 -e trace=openat,creat -c 2>&1
- 观察频繁写入或读取的文件名或路径模式。
/tmp/sess_abc123或/var/www/html/storage/logs/laravel.log。
- 观察频繁写入或读取的文件名或路径模式。
-
分析 PHP 代码中的 IO 热点
- 日志:检查是否有循环/高频的日志写入(如
error_log()、Monolog的Debug级别、var_dump到日志)。 - Session:如果不必要的 Session 写入(如每次请求都修改
$_SESSION),切换为 Redis/Memcached 存储。 - 文件缓存:检查框架的视图编译缓存、配置缓存是否每次请求都重新生成。
- 大文件处理:是否有
file_get_contents读取大文件、fwrite频繁写临时文件。
- 日志:检查是否有循环/高频的日志写入(如
第三步:常见 PHP 高 IO 场景与优化方向
| 场景 | 表现 | 解决方法 |
|---|---|---|
| Session 文件频繁读写 | IO 集中在 /tmp/sess_*,iowait 高 |
切换 Session 驱动到 Redis(session.save_handler = redis) |
| 框架 Debug 日志级别 | 日志文件每秒写入大量 DEBUG 行 | 生产环境日志级别设为 WARNING 或 ERROR |
| 视图编译缓存未开启 | 每次请求都重新编译模板(如 Laravel Blade) | 开启视图缓存(php artisan view:cache) |
| 文件上传/图片处理 | 批量 file_put_contents、imagejpeg |
使用队列异步处理,或使用 CDN/对象存储 |
| 慢查询+全表扫描 | 数据库查询慢但 IO 高(注意:PHP 进程可能只是在等待数据库返回,实际 IO 在 MySQL) | 检查数据库层面的 IO,而非 PHP 进程本身,使用 pt-ioprofile 或 MySQL 的 slow_query_log。 |
第四步:系统级快速排查命令清单(按优先级)
# 1. 实时查看磁盘 IO 最活跃的进程
iotop -oP
# 2. 查看某个分区上哪些进程在读写(如 /data)
ls -l /proc/*/fd/ 2>/dev/null | grep "/data" | awk '{print $10}' | sort | uniq -c | sort -rn
# 3. 针对 php-fpm 进程快速查看:
ps aux | grep php-fpm | awk '{print $2}' | xargs -I {} sh -c 'echo "PID={}"; ls -l /proc/{}/fd/ | grep "sess\|log\|cache"'
# 4. 如果是 Linux,使用 blktrace(高级)
blktrace -d /dev/sda -c 60 | blkparse -i - | grep php
典型排查流程
iotop -oP→ 看到php-fpm进程在大量写入。lsof -p [PID]→ 发现大量/tmp/sess_*句柄。- 判断 → Session 存储为文件,且并发高(每个请求都写 Session)。
- 解决 → 修改
php.ini:session.save_handler = redis,并检查session.auto_start设置,避免不必要写入。
如果问题不是 PHP 自身 IO,而是 PHP 进程被阻塞等待数据库/文件锁,那么表面上是 PHP 进程在消耗 IO,实际瓶颈在数据库或文件系统/NFS,这时需要结合 strace 观察系统调用是否卡在 connect、accept(网络等待)或 flock(文件锁)上。