PHP项目磁盘IO过高如何定位读写进程

wen PHP项目 27

本文目录导读:

PHP项目磁盘IO过高如何定位读写进程

  1. 第一步:确认是哪个磁盘分区和哪个进程
  2. 第二步:定位 PHP 进程内部行为
  3. 第三步:常见 PHP 高 IO 场景与优化方向
  4. 第四步:系统级快速排查命令清单(按优先级)
  5. 典型排查流程

在 PHP 项目中,定位磁盘 IO 过高的进程(特别是 PHP-FPM 或 CLI 进程)需要从系统级别进程级别入手,以下是分步排查方法:

第一步:确认是哪个磁盘分区和哪个进程

  1. 使用 iostat 查看磁盘整体负载(需要 sysstat 包)

    iostat -x 1 5
    • 关注 %util(磁盘利用率,接近 100% 说明繁忙)、await(IO 等待时间)、r/sw/s(读写次数)。
    • 如果特定分区(如 /dev/sda/dev/vda)很高,则进一步定位。
  2. 使用 iotop 实时查看进程 IO(推荐,需要 root)

    iotop -oP
    • -o:只显示有 IO 的进程。
    • -P:只显示进程(不显示线程)。
    • P 键按 IO 读写量排序,按 p 键切换进程/线程模式。
    • 重点看DISK READDISK WRITE 数值高的进程。
  3. 使用 pidstat 针对特定进程查看(需要 sysstat 包)

    # 先找到可疑的高 CPU/IO 进程 PID
    pidstat -d 1 5
    • 输出包括 kB_rd/skB_wr/s 等。

第二步:定位 PHP 进程内部行为

如果确认是 PHP 进程(如 php-fpmphp)导致的 IO 高,需要深入 PHP 代码层面:

  1. 查看 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.logerror.log
      • 文件缓存目录(如 Smarty、Twig 或 Laravel 视图缓存)
      • 图片上传、文件批量处理
  2. 开启 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
  3. 分析 PHP 代码中的 IO 热点

    • 日志:检查是否有循环/高频的日志写入(如 error_log()MonologDebug 级别、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 行 生产环境日志级别设为 WARNINGERROR
视图编译缓存未开启 每次请求都重新编译模板(如 Laravel Blade) 开启视图缓存(php artisan view:cache
文件上传/图片处理 批量 file_put_contentsimagejpeg 使用队列异步处理,或使用 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

典型排查流程

  1. iotop -oP → 看到 php-fpm 进程在大量写入。
  2. lsof -p [PID] → 发现大量 /tmp/sess_* 句柄。
  3. 判断 → Session 存储为文件,且并发高(每个请求都写 Session)。
  4. 解决 → 修改 php.inisession.save_handler = redis,并检查 session.auto_start 设置,避免不必要写入。

如果问题不是 PHP 自身 IO,而是 PHP 进程被阻塞等待数据库/文件锁,那么表面上是 PHP 进程在消耗 IO,实际瓶颈在数据库文件系统/NFS,这时需要结合 strace 观察系统调用是否卡在 connectaccept(网络等待)或 flock(文件锁)上。

抱歉,评论功能暂时关闭!