當網站響應變慢、CPU 飆高、請求堆積時,PHP-FPM 自帶的 Slow Log(慢請求日誌) 是排查性能問題的「核磁共振」。它能將執行超時的腳本調用棧完整記錄下來,幫助開發者快速定位具體卡在哪個函數、哪行代碼、哪個 SQL 查詢。本文詳解配置方法、日誌格式解讀、實戰分析技巧。
編輯 PHP-FPM 池配置文件(通常在 /etc/php-fpm.d/www.conf 或 /usr/local/php/etc/php-fpm.d/www.conf):
; 超過多少秒記錄慢日誌,0 關閉。單位:s(秒)、m(分)、h(時)、d(天)
request_slowlog_timeout = 1s
; 慢日誌文件路徑(確保 php-fpm 進程用戶有寫入權限)
slowlog = /usr/local/php/var/log/php-fpm.slow.log
修改後必須重載 PHP-FPM:
# systemd 系統
systemctl reload php-fpm
# 或傳統方式
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
生成的日誌條目示例:
[15-May-2024 10:30:45] [pool www] pid 12345
script_filename = /var/www/html/api/order/list.php
[0x00007f8a1c0008e0] PDOStatement->execute() /var/www/html/lib/Database.php:128
[0x00007f8a1c0007a0] Database->query() /var/www/html/lib/Database.php:210
[0x00007f8a1c000620] OrderService->getList() /var/www/html/service/OrderService.php:45
[0x00007f8a1c0004e0] ApiController->listAction() /var/www/html/controller/ApiController.php:30
[0x00007f8a1c0003a0] {main}() /var/www/html/api/order/list.php:15
| 字段 | 含義 |
|---|---|
[pool www] | FPM 池名稱 |
pid 12345 | 處理該請求的 Worker 進程 ID |
script_filename | 入口腳本完整路徑 |
[0x...] function() file:line | 調用棧幀:內存地址、函數/方法名、文件路徑、行號 |
| 順序 | 從下往上:最底層是入口,最頂層是當前正在執行(卡住)的函數 |
日誌最頂行(第一個 [0x...] 行)是請求超時時正在執行的函數。重點關注:
PDOStatement->execute()、mysqli_query()、Redis->get()curl_exec()、file_get_contents()、GuzzleHttp\Client->request()flock()、file_put_contents()、session_write_close()從頂層向下找到項目自有代碼(非框架/庫代碼),定位具體業務方法:
[0x...] OrderService->getList() /service/OrderService.php:45 ← 重點關注這行
[0x...] ApiController->listAction() /controller/ApiController.php:30
打開 OrderService.php:45,查看具體邏輯。
| 現象 | 可能原因 | 排查方向 |
|---|---|---|
| 卡在 PDO->execute() | SQL 執行慢、鎖等待、無索引、數據量大 | EXPLAIN SQL、檢查索引、慢查詢日誌、鎖分析 |
| 卡在 curl_exec() | 第三方接口響應慢、超時設置不當、DNS 解析慢 | 加超時、異步調用、熔斷降級、檢查 /etc/resolv.conf |
| 卡在 session_write_close() | Session 存儲後端慢、文件鎖競爭、Redis 連接慢 | 改用 Redis Session、檢查 session.save_path 權限 |
| 卡在自定義函數循環 | 算法複雜度高、死循環、大數據量未分頁 | 優化算法、添加斷點調試、引入生成器/分批處理 |
grep -c "request_slowlog" /var/log/php-fpm.slow.log 定期統計慢請求數量。request_terminate_timeout(如 30s)防止極端請求拖垮整個 Worker 池。# 提取 script_filename 並統計前 20
grep 'script_filename' /usr/local/php/var/log/php-fpm.slow.log \
| awk -F'= ' '{print $2}' | sort | uniq -c | sort -rn | head -20
Slow Log 有 pid 和時間戳,可在 Nginx 日誌中按時間查找對應請求的完整 URL、參數、User-Agent、客戶端 IP。
; 生產環境建議:2s-5s 之間,避免噪音過多
request_slowlog_timeout = 3s
; 同時開啟慢日誌與終止超時,防止 Worker 耗盡
request_terminate_timeout = 30s
; 日誌輪轉(logrotate 配置)
/usr/local/php/var/log/php-fpm.slow.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 640 www www
sharedscripts
postrotate
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid` 2>/dev/null || true
endscript
}
PHP-FPM Slow Log 是「零侵入、低開銷、高價值」的性能排查利器。正確配置、讀懂調用棧、結合業務代碼與基礎設施(數據庫、緩存、外部服務)綜合分析,能解決 80% 以上的線上性能疑難雜症。建議將其納入常規運維體系,配合告警實現「發現-定位-修復」閉環。