為什麼要將 Session 存入資料庫?
- 安全性:預設
/tmp 目錄權限過於開放,易遭本地使用者竊取 Session ID 冒用身分。
- 叢集支援:多台 Web Server 共享同一 Session 存儲,無需 NFS 掛載或 Sticky Session。
- 可控性:可自訂 GC 機制、加密儲存、稽核追蹤。
資料表結構
CREATE TABLE sesslib (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
sid VARCHAR(32) NOT NULL UNIQUE,
data TEXT,
time DATETIME,
INDEX idx_time (time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
自訂 Session Handler (XX.php)
<?php
$sess_dbh = null;
$sess_maxlifetime = ini_get("session.gc_maxlifetime");
function sess_open($save_path, $session_name) {
global $sess_dbh;
$sess_dbh = mysql_pconnect('localhost', 'test', 'test') or die("DB連線失敗");
mysql_select_db('test', $sess_dbh) or die("選擇資料庫失敗");
return true;
}
function sess_close() { return true; }
function sess_read($sid) {
global $sess_dbh;
$sid = mysql_real_escape_string($sid);
$res = mysql_query("SELECT data FROM sesslib WHERE sid='$sid'", $sess_dbh);
return ($row = mysql_fetch_row($res)) ? $row[0] : '';
}
function sess_write($sid, $sess_data) {
global $sess_dbh;
$sid = mysql_real_escape_string($sid);
$data = mysql_real_escape_string($sess_data);
$sql = "INSERT INTO sesslib (sid, data, time) VALUES ('$sid', '$data', NOW())
ON DUPLICATE KEY UPDATE data='$data', time=NOW()";
return mysql_query($sql, $sess_dbh);
}
function sess_destroy($sid) {
global $sess_dbh;
$sid = mysql_real_escape_string($sid);
return mysql_query("DELETE FROM sesslib WHERE sid='$sid'", $sess_dbh);
}
function sess_gc($maxlifetime) {
global $sess_dbh;
$limit = date('Y-m-d H:i:s', time() - $maxlifetime);
return mysql_query("DELETE FROM sesslib WHERE time < '$limit'", $sess_dbh);
}
// 註冊處理器
session_set_save_handler(
'sess_open', 'sess_close',
'sess_read', 'sess_write',
'sess_destroy', 'sess_gc'
);
session_start();
// 之後正常使用 $_SESSION
?>
關鍵優化點
- ON DUPLICATE KEY UPDATE:單一 SQL 處理新增/更新,避免競態條件。
- mysql_real_escape_string:防止 SQL 注入(舊版用
addslashes)。
- 索引優化:
sid 唯一索引加速查找;time 索引加速 GC 掃描。
- 資料加密:敏感資料可在 write/read 時搭配
openssl_encrypt/decrypt。
生產環境建議
- 使用 PDO/Mysqli 預處理語句替代
mysql_*。
- 啟用
session.use_strict_mode=1 防止 Session 固定攻擊。
- 搭配 Redis/Memcached 作為 L2 快取,降低 DB 壓力。