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

PDO 數據查詢緩存擴展實現方案

     編輯:PHP 相關知識

基於 PDO 擴展的 SQL 查詢結果緩存機制

在高並發 Web 應用中,數據庫查詢往往是性能瓶頸。本文介紹一個通過繼承 PDO 類實現的透明查詢緩存方案,支持自動緩存、過期機制、序列化存儲,可無縫替換原有 query() 調用,大幅降低數據庫負載。

核心類設計

class myPDO extends PDO {
    public $cache_Dir = null;           // 緩存目錄
    public $cache_expireTime = 7200;    // 緩存過期時間(秒),默認2小時
    
    // 帶緩存的查詢方法
    public function cquery($sql) {
        // 1. 校驗緩存目錄
        if ($this->cache_Dir === null || !is_dir($this->cache_Dir)) {
            exit("緩存目錄配置錯誤!");
        }
        $this->cache_Dir = str_replace("\\", "/", $this->cache_Dir);
        $FileName = rtrim($this->cache_Dir, "/") . "/" . urlencode(trim($sql)) . ".sql";
        
        // 2. 判斷是否需要重新查詢
        if (!file_exists($FileName) || time() - filemtime($FileName) > $this->cache_expireTime) {
            if ($tmpRS = parent::query($sql)) {
                $data = serialize($tmpRS->fetchAll());
                self::createFile($FileName, $data);
            } else {
                exit("SQL語法錯誤
"); } } // 3. 返回緩存結果 return $this->readCache($FileName); } // 讀取緩存文件 private static function readCache($FilePath) { if (is_file($FilePath) && $Data = file_get_contents($FilePath)) { return new cache_PDOStatement(unserialize($Data)); } return false; } // 創建緩存文件 public static function createFile($FilePath, $Data = '') { return file_put_contents($FilePath, $Data) !== false; } }

緩存結果集模擬類

class cache_PDOStatement {
    private $recordArr = [];
    private $cursorId = 0;
    private $recordCount = 0;
    
    public function __construct($arr) {
        $this->recordArr = $arr;
        $this->recordCount = count($arr);
    }
    
    // 模擬 PDOStatement::fetch()
    public function fetch() {
        if ($this->cursorId >= $this->recordCount) return false;
        if ($this->cursorId === 0) {
            $this->cursorId++;
            return current($this->recordArr);
        }
        $this->cursorId++;
        return next($this->recordArr);
    }
    
    // 模擬 PDOStatement::fetchAll()
    public function fetchAll() {
        return $this->recordArr;
    }
    
    // 模擬 PDOStatement::fetchColumn()
    public function fetchColumn() {
        $tmpArr = current($this->recordArr);
        return $tmpArr[0] ?? null;
    }
}

使用示例

<?php
// 1. 初始化連接
$db = new myPDO('mysql:host=localhost;dbname=news', 'newsadmin', '123456');

// 2. 配置緩存
$db->cache_Dir = "cache";           // 緩存目錄需可寫
$db->cache_expireTime = 7200;       // 2小時過期

// 3. 使用緩存查詢(替換原 query())
$rs = $db->cquery("SELECT * FROM news LIMIT 0,10");

while ($row = $rs->fetch()) {
    echo $row["F_title"] . "
"; } // 4. 清理資源 $rs = null; $db = null; ?>

關鍵設計亮點

  • 透明兼容:返回對象模擬 PDOStatement 接口,業務代碼無需修改
  • SQL 作為鍵值:使用 urlencode($sql) 作為文件名,自動區分不同查詢
  • 文件修改時間判斷過期:無需額外存儲元數據,利用文件系統特性
  • 序列化存儲:支持任意數據結構,還原後保持關聯數組格式

潛在問題與改進方向

問題風險等級改進建議
SQL 注入風險高緩存鍵使用原始 SQL,若含用戶輸入可能導致緩存污染或路徑遍歷
緩存鍵過長中文件系統對文件名長度有限制,建議使用 hash($sql) 作為文件名
無緩存清理機制中需定期清理過期文件,避免磁盤佔用過大
並發寫入競爭低高並發下可能重複查詢,可加文件鎖或雙重檢查
不支持參數綁定高無法緩存預處理語句,建議擴展支持 prepare()/execute()

改進版緩存鍵生成策略

// 使用 SHA256 哈希避免文件名過長及特殊字符問題
private function getCacheKey($sql, $params = []) {
    $keyData = $sql . serialize($params);
    return hash('sha256', $keyData) . '.cache';
}

// 支持預處理語句緩存
public function cexecute($stmt, $params = []) {
    $cacheKey = $this->getCacheKey($stmt->queryString, $params);
    // ... 其餘邏輯類似
}

生產環境部署建議

  1. 緩存目錄權限:確保 Web 進程有讀寫權限,建議放在項目外部不可訪問路徑
  2. 內存文件系統:高性能場景可掛載 tmpfs/ramdisk 作為緩存目錄
  3. 監控指標:監控緩存命中率、文件數量、磁盤佔用
  4. 優雅降級:緩存失效時自動回退到數據庫查詢,不拋出異常
  5. 分佈式擴展:集群環境下建議遷移至 Redis/Memcached 共享緩存

這個方案雖然簡單,但展示了「裝飾器模式」在數據庫訪問層的實際應用,是理解 ORM 緩存機制、查詢優化的良好切入點。

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