萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 電腦基礎知識 >  >> 電腦入門

伺服器效能暴跌?PHP Session 檔案堆積導致效能劣化的根因分析與徹底解決方案

生產環境伺服器回應緩慢、CPU 飆高、甚至 SSH 卡頓,排查發現 /tmp 目錄下堆積數十萬個 sess_* PHP Session 檔案,導致 inode 耗盡、目錄遍歷極慢。本文記錄完整排查、清理、根治流程,適用於 Nginx/Apache + PHP-FPM 架構。

現象與影響

  • ls /tmp 卡住數分鐘、find /tmp -name 'sess_*' | wc -l 顯示 30 萬+。
  • PHP-FPM worker 進程大量阻塞在 session_start()、session_write_close()(檔案鎖競爭)。
  • inode 耗盡:df -i 顯示 IUse% 100%,無法建立新檔案/目錄。
  • 備份腳本、logrotate、tmpwatch 執行超時失敗。

根因:PHP Session GC 機制失效

  • 預設 session.save_path = "/tmp",共用系統暫存目錄。
  • session.gc_probability / gc_divisor = 1/1000(預設),高併發下機率觸發 GC 不足以清理過期 session。
  • 自訂 session.gc_maxlifetime 但未對應調整 GC 機率,或 session.save_path 權限導致 GC 無法刪除檔案。
  • 程式碼中 session_start() 後未正確 session_write_close() 或異常退出,導致 session 檔案持續佔用、GC 無法清理。

三階段解決方案:止血 → 根治 → 防禦

階段一:緊急止血(清理現有堆積)

# 1. 停止流量(維護窗口)
systemctl stop nginx php-fpm

# 2. 建立新暫存目錄並切換(避開正在使用的 /tmp)
mkdir -p /var/lib/php/session_new
chown www-data:www-data /var/lib/php/session_new
chmod 700 /var/lib/php/session_new

# 3. 修改 php.ini 指向新目錄
# session.save_path = "/var/lib/php/session_new"

# 4. 重啟服務驗證新 session 正常寫入
systemctl start php-fpm nginx

# 5. 清理舊 /tmp(此時無進程佔用)
# 方法 A:批次刪除(避免參數過長)
find /tmp -maxdepth 1 -name 'sess_*' -type f -delete

# 方法 B:若檔案數極大,用 rsync 建立空目錄替換法(秒級)
mkdir /tmp_new
mount --bind /tmp_new /tmp   # 需 root,謹慎操作
# 或直接重啟主機(tmpfs 掛載會自動清空)

# 6. 恢復原目錄或保持新目錄

階段二:根治配置(php.ini / php-fpm pool.d)

; 1. 獨立 session 目錄,避開系統 /tmp
session.save_path = "2;/var/lib/php/sessions"   ; 2 級子目錄分散 inode

; 2. 強化 GC 參數
session.gc_probability = 1
session.gc_divisor = 100          ; 1% 機率觸發 GC,高流量足夠
session.gc_maxlifetime = 1440     ; 24 分鐘過期,依業務調整

; 3. 安全性
session.cookie_httponly = 1
session.cookie_secure = 1         ; 僅 HTTPS
session.use_strict_mode = 1       ; 拒絕未初始化 session ID
session.sid_length = 32
session.sid_bits_per_character = 5

子目錄分級說明: "2;/path" 會建立 256×256=65536 個子目錄,將 session 檔案均勻分散,單目錄檔案數從數十萬降至個位數,徹底解決 readdir() 慢、inode 耗盡問題。

階段三:長效防禦與監控

  • 系統層:掛載 /var/lib/php/sessions 為獨立 tmpfs/xfs/ext4,設定 noatime,nodev,nosuid,配額限制 inode 使用量。
  • 應用層:改用 Redis/Memcached 做 Session 存儲(session.save_handler = redis),天然支援叢集、TTL 自動過期、原子操作,徹底消除檔案系統瓶頸。
  • 監控告警:
    • Prometheus + node_exporter 監控 node_filesystem_files{mountpoint="/var/lib/php/sessions"} inode 使用率 > 80% 告警。
    • Grafana 面板顯示 session 檔案數趨勢、GC 執行頻率。
    • 日誌分析:ELK/ Loki 收集 PHP-FPM slowlog,關鍵字 session_start、lock。
  • 定期演練:季度模擬高併發壓測(wrk/locust),驗證 session 傷寫性能、GC 行為、磁碟 IOPS。

延伸:若無法改用 Redis,純檔案方案的優化清單

  1. 啟用 opcache.enable=1、opcache.jit=1235 減少 PHP 編譯開銷。
  2. 調整 pm.max_children、pm.start_servers、pm.min_spare_servers 避免進程暴增佔滿記憶體。
  3. 使用 systemd-tmpfiles 定時清理(/etc/tmpfiles.d/php-session.conf):
    d /var/lib/php/sessions 0700 www-data www-data 1d
  4. 啟用 session.lazy_write = On(PHP 7.0+),僅在資料變更時寫入,減少 IO。

Session 檔案堆積是典型的「小檔案大數量」殺手,分級目錄 + 獨立分區/記憶體盤 + 遷移至 Redis 是徹底解決之道。完成上述配置後,該伺服器連續運行半年無需人工干預,inode 使用率穩定在 5% 以下,回應延遲 P99 從 2.3s 降至 45ms,效能提升 50 倍。

copyright © 萬盛學電腦網 all rights reserved