預設 PHP Session 存儲於本機檔案 (session.save_handler = files),多台 Web 伺服器無法共享 Session,導致使用者在負載平衡切換伺服器時登入狀態丟失。
| 方案 | 原理 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| Nginx IP Hash / Sticky Session | 同一 Client 固定導向同一後端 | 零程式碼修改 | 伺服器下線 Session 遺失、負載不均 | 小型、可容忍短暫掉線 |
| 資料庫共享 (MySQL/PostgreSQL) | 自訂 Session Handler 寫入 DB | 持久化、可查詢、ACID | 寫入延遲、連線池壓力 | 中型、需稽核追蹤 |
| Redis / Memcached | 記憶體 Key-Value,TTL 自動過期 | 高效能、原子操作、叢集擴展 | 資料非持久、需 HA 設計 | 高併發、大流量 (推薦) |
| NFS/GlusterFS 共享檔案系統 | 掛載共享目錄存放 session 檔案 | 相容原生 files handler | 單點故障、延遲高、鎖競爭 | 舊系統快速遷移 |
apt install php-redis
# 或 pecl install redis
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=PASSWORD&database=0&prefix=PHPSESSID:"
# 叢集/哨兵
# session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=1"
session.gc_maxlifetime = 1440
";
echo "Server: " . gethostname() . "
";
$_SESSION['counter'] = ($_SESSION['counter'] ?? 0) + 1;
echo "Counter: " . $_SESSION['counter'];?>
於多台伺服器瀏覽,Counter 應同步遞增。
pdo = $pdo; }
public function open($path, $name): bool { return true; }
public function close(): bool { return true; }
public function read($id): string|false {
$stmt = $this->pdo->prepare('SELECT data FROM sessions WHERE id = ? AND expiry > ?');
$stmt->execute([$id, time()]);
return $stmt->fetchColumn() ?: '';
}
public function write($id, $data): bool {
$expiry = time() + ini_get('session.gc_maxlifetime');
$stmt = $this->pdo->prepare('INSERT INTO sessions (id, data, expiry) VALUES (?,?,?)
ON DUPLICATE KEY UPDATE data=?, expiry=?');
return $stmt->execute([$id, $data, $expiry, $data, $expiry]);
}
public function destroy($id): bool {
return $this->pdo->prepare('DELETE FROM sessions WHERE id = ?')->execute([$id]);
}
public function gc($maxlifetime): int|false {
return $this->pdo->prepare('DELETE FROM sessions WHERE expiry < ?')->execute([time()]);
}
}
// 註冊
$handler = new DbSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();
預設 PHP 為同一 Session ID 加鎖 (阻塞並發請求)。高併發場景可:
session_write_close() 盡早釋放鎖 (資料已寫入)session.save_handler = redis (Redis 內建非阻塞或可配置)session_start(['read_only' => true]) (PHP 7+)