PHP 默認使用文件存儲 Session (`session.save_handler = files`)。調用 `session_start()` 時,PHP 會對 Session 文件加**獨占鎖 (Exclusive Lock)**。同一客戶端 (同 Session ID) 並發請求會導致串行執行,嚴重影響響應速度。
<?php // page_a.php - 耗時 5 秒 session_start(); sleep(5); echo "A done"; session_write_close(); // page_b.php - 並發請求 session_start(); // 此處阻塞,等待 page_a 釋放鎖 echo "B done"; ?>
瀏覽器同時打開兩頁,B 頁需等待 5 秒才開始輸出。異步 AJAX 請求同理,全部串行化。
<?php session_start(); // 讀取 Session 數據 $user = $_SESSION['user']; // ✅ 立即釋放鎖,後續耗時操作不阻塞其他請求 session_write_close(); // 耗時操作:數據庫查詢、外部 API 調用、大文件處理... sleep(3); $heavyData = fetchHeavyData(); // ⚠️ 此後不能再寫入 $_SESSION,需重新 session_start() 才能寫入 // 但重新 start 會再次加鎖,建議將寫操作前置 ?>
適用場景:讀取 Session 後執行耗時邏輯,無需寫回 Session。
; php.ini session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379?auth=password&database=0" ; 或 Memcached session.save_handler = memcached session.save_path = "127.0.0.1:11211"
優勢:
• 無文件鎖,天然支援高併發
• 分布式部署共享 Session
• 可配置 TTL 自動過期清理
• 性能遠超文件 IO
<?php
class DatabaseSessionHandler implements SessionHandlerInterface {
private $pdo;
public function __construct(PDO $pdo) { $this->pdo = $pdo; }
public function open($path, $name) { return true; }
public function close() { return true; }
public function read($id) {
// SELECT data FROM sessions WHERE id = ? AND expire > NOW()
// 無行鎖,使用行級鎖或樂觀鎖更新
}
public function write($id, $data) {
// INSERT ... ON DUPLICATE KEY UPDATE ...
}
public function destroy($id) { /* DELETE */ }
public function gc($maxlifetime) { /* DELETE expired */ }
}
$handler = new DatabaseSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();
?>
可在 `write` 中使用 `SELECT ... FOR UPDATE` 或版本號樂觀鎖實現細粒度併發控制。
| 框架 | 默認行為 | 優化建議 |
|---|---|---|
| Laravel | 文件驅動,請求結束自動保存 | 生產環境改用 Redis/Database 驅動 |
| Symfony | Native PHP Session | 配置 `session.handler.service` 使用 Redis |
| ThinkPHP | 文件驅動 | 配置 `session_handler` 為 Redis |
| Yii2 | Session 組件 | 使用 `yii\redis\Session` |