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

PHP Session 存入資料庫完整實作指南

     編輯:PHP 綜合知識

為什麼要將 Session 存入資料庫?

預設 PHP 以檔案儲存 Session(session.save_handler = files),單機環境尚可,但在負載均衡、多伺服器叢集或容器化部署時,檔案系統無法共享,導致使用者頻繁被登出。將 Session 寫入資料庫(或 Redis/Memcached)即可實現跨節點共享,並便於集中管理、查詢與清理。

實作步驟

1. 修改 php.ini 或運行時設定

; 採用自定義儲存處理器
session.save_handler = user

或在程式啟動早期呼叫:

ini_set('session.save_handler', 'user');

2. 建立資料表結構(MySQL 範例)

CREATE DATABASE `session_db` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE `session_db`;

CREATE TABLE `sessions` (
    `id`        CHAR(128) NOT NULL PRIMARY KEY,
    `user_id`   BIGINT UNSIGNED NULL DEFAULT NULL,
    `data`      MEDIUMBLOB NOT NULL,
    `last_activity` INT UNSIGNED NOT NULL DEFAULT 0,
    INDEX `idx_last_activity` (`last_activity`),
    INDEX `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • id:Session ID,長度 128 相容 PHP 7.1+ session.sid_length。
  • user_id:關聯使用者,方便踢除特定用戶 Session。
  • data:序列化後的 Session 資料。
  • last_activity:最後活動時間戳,供 GC 清理過期資料。

3. 實作自定義 Session 處理器

<?php
declare(strict_types=1);

class PdoSessionHandler implements SessionHandlerInterface
{
    private PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function open($savePath, $sessionName): bool
    {
        return true; // PDO 連線已在外部建立
    }

    public function close(): bool
    {
        return true;
    }

    public function read($id): string|false
    {
        $stmt = $this->pdo->prepare('SELECT `data` FROM `sessions` WHERE `id` = ?');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ? $row['data'] : '';
    }

    public function write($id, $data): bool
    {
        $time = time();
        $sql = <<pdo->prepare($sql);
        return $stmt->execute([$id, $data, $time]);
    }

    public function destroy($id): bool
    {
        $stmt = $this->pdo->prepare('DELETE FROM `sessions` WHERE `id` = ?');
        return $stmt->execute([$id]);
    }

    public function gc($maxLifetime): int|false
    {
        $expiry = time() - $maxLifetime;
        $stmt = $this->pdo->prepare('DELETE FROM `sessions` WHERE `last_activity` < ?');
        $stmt->execute([$expiry]);
        return $stmt->rowCount();
    }
}

// 註冊處理器
$pdo = new PDO('mysql:host=127.0.0.1;dbname=session_db;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

$handler = new PdoSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();

// 後續正常使用 $_SESSION
$_SESSION['user_id'] = 123;

4. 進階優化建議

  • 改用 Redis:高併發場景下,Redis 記憶體操作效能遠超 MySQL,並原生支援 TTL 自動過期。
  • 加密敏感資料:Session 若存放權限標識,建議寫入前加密、讀取後解密。
  • 分散式鎖防併發寫入:極高併發時可引入 Redlock 或資料庫行鎖避免資料競爭。
  • 定期清理:雖有 GC 機制,仍建議排程清理孤兒資料(如 last_activity 超過 30 天)。

常見坑點提醒

  1. session_set_save_handler() 必須在 session_start() 之前呼叫。
  2. 資料庫連線失敗時,read() 應回傳空字串而非 false,否則 PHP 會拋出警告。
  3. 若使用 session_regenerate_id(true),舊 Session 資料需同步刪除或標記失效。

結語

將 Session 遷移至資料庫是水平擴展的關鍵一步。本文提供基於 PDO 的完整實作,可直接套用於生產環境;若追求極致效能,建議進一步改用 Redis Cluster 作為儲存後端。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved