首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

PHP Session 存儲遷移 MySQL 實戰:自定義處理器實現分布式會話共享

     編輯:MySQL 綜合教學

前言

默認文件存儲 Session 面臨:多機負載均衡無法共享、大量小文件 Inode 耗盡、I/O 瓶頸、清理機制不可控。將 Session 遷移至 MySQL(或 Redis)是分布式應用的標準解法。本文基於 PHP session_set_save_handler() 實現完整 MySQL 存儲處理器,含表設計、六大回調函數、GC 機制、併發安全、性能優化要點。

一、Session 表設計

CREATE TABLE `php_session` (
  `session_id` VARCHAR(128) NOT NULL PRIMARY KEY COMMENT 'PHPSESSID',
  `session_data` TEXT NOT NULL COMMENT '序列化後的 $_SESSION',
  `session_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最後訪問時間',
  `expire_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '過期時間戳,冗余字段加速 GC'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='PHP Session 存儲表';

索引策略:主鍵 session_id 滿足點查;expire_time 建普通索引加速 DELETE WHERE expire_time < NOW()。

二、核心實現代碼(PDO 版,生產就緒)

<?php
class MySqlSessionHandler implements SessionHandlerInterface {
    private $pdo;
    private $table = 'php_session';
    private $gcProbability = 1;
    private $gcDivisor = 100;      // 1% 請求觸發 GC
    private $gcMaxLifetime = 1440; // 24 分鐘

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    // 1. open: 初始化連接(這裡已在構造函數完成)
    public function open($savePath, $sessionName): bool { return true; }

    // 2. close: 關閉連接
    public function close(): bool { return true; }

    // 3. read: 讀取會話數據
    public function read($sessionId): string {
        $stmt = $this->pdo->prepare("SELECT session_data FROM {$this->table} WHERE session_id = ? AND expire_time > UNIX_TIMESTAMP()");
        $stmt->execute([$sessionId]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ? $row['session_data'] : '';
    }

    // 4. write: 寫入/更新會話數據(UPSERT)
    public function write($sessionId, $data): bool {
        $expire = time() + $this->gcMaxLifetime;
        $sql = "INSERT INTO {$this->table} (session_id, session_data, expire_time) VALUES (?, ?, ?)
                ON DUPLICATE KEY UPDATE session_data = VALUES(session_data), expire_time = VALUES(expire_time)";
        $stmt = $this->pdo->prepare($sql);
        return $stmt->execute([$sessionId, $data, $expire]);
    }

    // 5. destroy: 顯式銷毀
    public function destroy($sessionId): bool {
        $stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE session_id = ?");
        return $stmt->execute([$sessionId]);
    }

    // 6. gc: 垃圾回收
    public function gc($maxLifetime): int|false {
        $stmt = $this->pdo->prepare("DELETE FROM {$this->table} WHERE expire_time < UNIX_TIMESTAMP()");
        $stmt->execute();
        return $stmt->rowCount();
    }
}

// 註冊處理器
$pdo = new PDO('mysql:host=127.0.0.1;dbname=session_db;charset=utf8mb4', 'session_user', 'pass');
$handler = new MySqlSessionHandler($pdo);
session_set_save_handler($handler, true);

// 可選:調整 GC 概率
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', 1440);

// 啟動會話
session_start();
$_SESSION['user_id'] = 12345;
?>

三、關鍵細節深度解析

3.1 序列化格式

PHP 內部使用 session.serialize_handler(默認 php,也可 php_binary、igbinary)。read 返回的字符串必須與 write 存入的格式完全一致,否則反序列化失敗導致 Session 數據丟失。

3.2 併發安全:寫入鎖與樂觀鎖

  • PHP 請求結束前調用 write,同一 Session 併發請求可能並發寫入
  • 方案 A:SELECT ... FOR UPDATE 悲觀鎖(增加延遲)
  • 方案 B(推薦):ON DUPLICATE KEY UPDATE 無鎖覆蓋,最後寫入勝出,Session 語義允許
  • 方案 C:引入 version 列樂觀鎖,衝突重試

3.3 GC 策略優化

  • 默認概率觸發(gc_probability/gc_divisor)不可控,高併發下可能長期不執行
  • 生產建議:關閉 PHP 自動 GC(session.gc_probability=0),改用 Cron 定時任務 每分鐘執行 DELETE FROM php_session WHERE expire_time < UNIX_TIMESTAMP()
  • 大表分區按天/小時,DROP PARTITION 秒級清理歷史數據

3.4 性能優化清單

  1. 連接池:PHP-FPM 持久連接 PDO::ATTR_PERSISTENT = true 或 Swoole 協程池
  2. 讀寫分離:Session 讀頻繁,走從庫;寫走主庫(中間件路由)
  3. 壓縮:session_data 大時 gzcompress/gzuncompress 節省存儲/網絡
  4. 異步寫入:寫入發送到消息隊列,消費者批量入庫,請求無感知

四、遷移清單與驗證

  1. 建表、授權 Session 專用賬號(僅需增刪改查 php_session 表)
  2. 部署新處理器代碼,灰度驗證 session_start()、讀寫、銷毀、GC
  3. 監控:Session 表行數、GC 耗時、死鎖數、主從延遲
  4. 全量切換,下線文件存儲目錄
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved