預設所有 Session 文件存放在同一目錄(如 /tmp 或 /var/lib/php/session)。高併發下文件數達萬/十萬,導致:
多級目錄將文件按 Session ID 前綴分散到子目錄,單目錄文件數驟降。
; 格式:層級;路徑
; 3 級目錄,每級 1 個字符(取決於 hash_bits_per_character)
session.save_path = "3;/var/lib/php/sessions"
PHP 源碼 ext/session/mod_files.sh 腳本專用於此:
# 語法:bash mod_files.sh [hash_bits_per_character]
# 例如:3 級,hash_bits=4 (默認)
bash mod_files.sh /var/lib/php/sessions 3 4
# 若 php.ini 設置了 session.hash_bits_per_character = 5 (PHP 7.1+ 默認)
bash mod_files.sh /var/lib/php/sessions 3 5
原文踩坑點:作者最初只執行
bash mod_files.sh /tmp/session 3,漏掉第 4 參數5,導致目錄結構按 4 位創建,而運行時按 5 位查找,**目錄不匹配導致 Session 讀寫失敗**。
session.hash_bits_per_character 取值:4 (0-9,a-f, 16 進制)、5 (0-9,a-v, 32 進制)、6 (0-9,a-z,A-Z,-, 64 進制)。<?php
session_start();
echo 'Session ID: ' . session_id() . '<br>';
echo 'Save Path: ' . ini_get('session.save_path') . '<br>';
// 手動檢查文件是否落在正確子目錄
$sid = session_id();
$depth = 3;
$bits = ini_get('session.hash_bits_per_character'); // 或 session.sid_bits_per_character (PHP 7.1+)
$chars = ($bits == 4) ? 1 : (($bits == 5) ? 1 : 1); // 簡化:每級取 1 字符
$path = ini_get('session.save_path');
$path = preg_replace('/^\d+;/', '', $path); // 去掉 "3;"
$file = $path;
for ($i = 0; $i < $depth; $i++) {
$file .= '/' . $sid[$i];
}
$file .= '/sess_' . $sid;
echo '預期文件: ' . $file . ' -> ' . (file_exists($file) ? '存在' : '不存在');
session.save_handler = redis,session.save_path = "tcp://127.0.0.1:6379?prefix=sess:"。彻底解決文件系統瓶頸,支援集群、自動過期、高併發。tmpfs (內存盤) 掛載 Session 目錄:mount -t tmpfs -o size=512m tmpfs /var/lib/php/sessions。session.gc_probability = 1, session.gc_divisor = 1000 (0.1% 機率觸發 GC),配合系統 cron 定時清理舊文件:find /var/lib/php/sessions -type f -mmin +24 -delete。df -i。www-data:www-data,權限 700 或 1700 (sticky bit),防止其他用戶讀取/刪除 Session 文件。多級目錄是文件 Session 的「止血方案」,核心在於 php.ini 深度 + mod_files.sh 創建目錄時指定 hash_bits 參數 必須**嚴格一致**。現代架構建議直接上 Redis,一勞永逸。