PHP 預設以檔案儲存 Session,面臨兩大痛點:檔案 I/O 效能低、多伺服器環境下 Session 不共享。將 Session 存入 Redis 可同時解決這兩問題。本文提供兩種實作方式:php.ini 簡單配置法與自訂 SessionHandler 類別法,並解析核心原理。
優點:零程式碼修改,立即生效。缺點:無法自訂序列化、GC 策略、集群容錯。
方案二:自訂 RedisSession 類別(生產環境推薦,完全可控)
null, 'host' => '127.0.0.1', 'port' => 6379, 'auth' => '', // 可選 'db' => 0, // 可選 'prefix' => 'sess_', // 鍵前綴,避免衝突 ); public function __construct($config = array()) { $this->_redis = array_merge($this->_redis, $config); } public function register() { session_set_save_handler( array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc') ); // 可選:關閉 PHP 內建 GC,由 Redis TTL 自動過期 ini_set('session.gc_probability', 0); session_start(); } // 1. 開啟連線 public function open() { $redis = new Redis(); $redis->connect($this->_redis['host'], $this->_redis['port']); if ($this->_redis['auth']) $redis->auth($this->_redis['auth']); if ($this->_redis['db']) $redis->select($this->_redis['db']); $this->_redis['handler'] = $redis; return true; } // 2. 關閉連線 public function close() { return $this->_redis['handler']->close(); } // 3. 讀取 Session 資料 public function read($session_id) { $key = $this->_redis['prefix'] . $session_id; $data = $this->_redis['handler']->get($key); return $data !== false ? $data : ''; } // 4. 寫入 Session 資料(自動設定 TTL) public function write($session_id, $session_data) { $key = $this->_redis['prefix'] . $session_id; $ttl = ini_get('session.gc_maxlifetime') ?: 1440; // 預設 24 分鐘 return $this->_redis['handler']->setex($key, $ttl, $session_data); } // 5. 銷毀 Session public function destroy($session_id) { $key = $this->_redis['prefix'] . $session_id; return $this->_redis['handler']->del($key) > 0; } // 6. 垃圾回收(Redis 靠 TTL 自動過期,此處可留空或掃描大鍵) public function gc($maxlifetime) { // 可選:SCAN 大鍵清理,但通常不需要 return true; } } // 使用範例 $session = new RedisSession(array( 'host' => '127.0.0.1', 'port' => 6379, 'auth' => 'your_redis_password', 'prefix' => 'myapp_sess_', )); $session->register(); $_SESSION['user_id'] = 12345; echo "Session 寫入完成";關鍵優化細節
- 鍵前綴:避免多應用共用 Redis 時衝突。
- SETEX 原子寫入:
setex(key, ttl, value)確保資料與過期時間原子性寫入,避免殭屍 Session。- 關閉 PHP 內建 GC:
ini_set('session.gc_probability', 0),完全交由 Redis TTL 管理,減少隨機掃描效能抖動。- 叢集支援:使用
phpredis叢集模式或Predis客戶端,配置多節點實現高可用。- 序列化優化:大量資料時改用
igbinary或msgpack替代預設serialize。常見問題排查
| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| Session 頻繁遺失 | Redis 記憶體不足觸發淘汰策略 | 調整 maxmemory-policy 為 allkeys-lru,增加記憶體 |
| 多伺服器 Session 不同步 | 未統一 session.save_path | 所有節點指向同一 Redis 叢集 |
| Session 資料截斷 | 序列化後超過 Redis 512MB 單鍵限制 | 拆分大物件、壓縮序列化 |
| 效能抖動 | PHP GC 隨機觸發掃描 | 關閉 PHP GC,依賴 Redis TTL |
將 Session 遷移至 Redis 是 PHP 應用水平擴展的關鍵步驟。建議生產環境採用方案二自訂類別,配合監控告警(記憶體使用率、連線數、指令延遲),打造高可用、高效能的 Session 基礎設施。