PHP项目如何排查服务器CPU过高?

wen PHP项目 55

本文目录导读:

PHP项目如何排查服务器CPU过高?

  1. 第一步:定位高 CPU 进程
  2. 第二步:分析 PHP-FPM 进程
  3. 第三步:分析应用代码(常见导致 CPU 高的 PHP 场景)
  4. 第四步:使用专业性能分析工具(进阶)
  5. 标准的排查路径

排查 PHP 项目导致的服务器 CPU 过高问题,通常需要从系统层面PHP 进程层面应用代码层面逐层深入,以下是系统化的排查步骤和常用命令:

第一步:定位高 CPU 进程

确认是高 CPU 是哪个进程引起的。

  1. 使用 tophtop 命令:
    top -c
    • P 键(大写 P),让进程按 CPU 使用率排序。
    • 观察占用 CPU 最高的进程是 php-fpm(或 apache/nginx 的 worker 进程)还是 MySQL、Redis 等其他服务。
    • 常见结果 1: 全是 php-fpm 进程。 → 问题出在 PHP 代码或 PHP-FPM 配置。
    • 常见结果 2: MySQL 进程很高。 → 往往是 PHP 代码中的慢查询导致。
    • 常见结果 3: Nginx/Apache 进程很高。 → 可能是静态资源问题或 DDOS 攻击。

第二步:分析 PHP-FPM 进程

如果确认是 php-fpm 导致 CPU 高,需要查看是哪个 PHP 请求在“吃”CPU。

1 使用 strace 追踪系统调用(适合快速确认瓶颈)

对高 CPU 的 PHP 进程 PID(假设为 12345)执行:

strace -p 12345 -c -S time 2>&1
# 运行几秒后按 Ctrl+C 看统计
  • 如果看到大量 pollselectepoll_wait → 可能是在等待 I/O(数据库、Redis、文件)。
  • 如果看到大量 clock_gettimemmap → 可能是在执行密集计算或循环。
  • 如果看到大量 lstatopenread → 可能是频繁的 file_exists()is_file()include/require 操作。

2 使用 gdb 或简单查看调用栈(更精准)

# 查看 PHP 进程正在执行的代码(PHP 有调用栈)
gdb -p <PID> -batch -ex "bt" 2>/dev/null | head -20
# 或者更常用的方式,利用 PHP-FPM 的 pm.status_path 查看活跃请求
# 在 nginx 配置中添加:
# location ~ ^/php-fpm-status$ { 
#     include fastcgi_params;
#     fastcgi_pass unix:/var/run/php-fpm.sock; 
#     fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; 
# }
# 访问 /php-fpm-status?full 查看正在执行的脚本
curl http://localhost/php-fpm-status?full

3 检查慢日志(最直接的方法)

CPU 高是间歇性的,慢日志是宝库。

  1. 开启 PHP-FPM 慢日志:
    ; php-fpm.conf 或 www.conf
    request_slowlog_timeout = 5  ; 超过 5 秒记录慢日志
    slowlog = /var/log/php-fpm/slow.log
    request_terminate_timeout = 60 ; 超时 60 秒强制终止
  2. 分析慢日志:
    tail -f /var/log/php-fpm/slow.log
    • 重点看: 频繁出现的脚本路径 + 具体的函数调用堆栈(如 file_get_contentscurl_execpreg_match 在循环中)。

第三步:分析应用代码(常见导致 CPU 高的 PHP 场景)

  1. 死循环或无限递归:

    • 常见于 while(true) 未正确跳出,或递归函数未写好终止条件。
    • 排查: 查看慢日志中永远卡在某个函数内部。
  2. 低效的数据库查询(N+1 问题):

    • 在循环内部执行 SQL 查询(如 foreach($users as $u) { $db->query(...) })。
    • 排查: 开启 MySQL 慢查询日志,配合 pt-query-digest 分析。
  3. 频繁的文件操作:

    • file_exists()is_file() 每次读取 Session 或缓存。
    • 排查: strace 看到大量 lstat/open 调用。
    • 解决: 使用 realpath_cache_size 或改用 Redis/Memcached 存储 Session。
  4. 正则表达式回溯(ReDoS 攻击):

    • 输入恶意字符串使得 preg_match() 进入灾难性回溯。
    • 排查: 查看慢日志,卡在 preg_matchpreg_replace
    • 解决: 使用 preg_match('/pattern/', $str, $match, PREG_UNMATCHED_AS_NULL) 或限制输入长度。
  5. 大量计算请求(加密、图像处理):

    • openssl_encryptimagick 操作大图、json_decode 超大文件、base64_decode 巨量数据。
  6. 僵尸进程或配置不当:

    • pm.max_children 设置得太高,导致 PHP-FPM 进程数过多,CPU 抢占严重。
    • pm.start_servers 设置过大。

第四步:使用专业性能分析工具(进阶)

  • Xdebug + KCachegrind: 在开发环境或低流量服务器上,临时开启 Xdebug 的 profiler:

    xdebug.mode = profile
    xdebug.output_dir = /tmp

    生成 cachegrind.out.* 文件,用 QCacheGrind 工具打开,可以看到哪个函数消耗 CPU 最多

  • Tideways/XHProf(在线分析): 适用于生产环境,可以实时查看请求的 CPU 耗时、内存占用、函数调用次数。

  • New Relic / Datadog(APM 工具): 如果预算允许,这是最省力的方法,直接显示哪个 URL、哪个数据库查询最慢。

标准的排查路径

graph TD
    A[服务器CPU告警] --> B{使用 top 看进程}
    B --> C[MySQL进程高]
    B --> D[其他进程高]
    B --> E[php-fpm进程高]
    C --> F[检查慢查询日志,优化SQL/加索引]
    E --> G[开启PHP-FPM慢日志]
    G --> H[检查慢日志中的脚本和堆栈]
    H --> I[分析具体问题:死循环/N+1/正则/文件操作]
    I --> J[修复代码/优化配置]
    D --> K[检查是否为攻击/日志进程/备份进程]

最后提醒: 如果是周期性(如每小时的某分钟)CPU 瞬间飙高,优先排查计划任务(Cron Job),如果是持续高,优先排查前端 API 接口爬虫

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