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

PHP 快取技術實戰:從檔案快取到 Redis 的完整演進指南

     編輯:PHP 綜合知識

快取核心原理

快取本質是「用空間換時間」:將「經常讀取、較少變動、生成成本高」的資料預先計算並儲存,後續請求直接讀取快取,避免重複資料庫查詢或複雜運算。

JBLOG 實戰案例分析

某部落格系統透過快取將單頁資料庫查詢從 15+ 次降至 3 次,快取項目包括:

  • 系統設定
  • 文章分類
  • 側邊欄最新文章/評論
  • 站點統計、文章歸檔
  • 友情連結、標籤雲

核心快取類實作

1. 快取管理類 Cache.php

<?php
namespace Core;

class Cache
{
    protected $cacheDir;
    protected $allowedKeys = [
        'config', 'class', 'archive', 'newcomment', 
        'newpost', 'link', 'tag', 'statistic', 'topblog'
    ];
    
    public function __construct($cacheDir = null)
    {
        $this->cacheDir = $cacheDir ?? ROOT_PATH . 'cache_data/';
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 0755, true);
        }
    }
    
    // 刷新指定快取
    public function refresh($keys = '')
    {
        $list = $keys ? explode(',', $keys) : $this->allowedKeys;
        foreach ($list as $key) {
            $method = $key . '_refresh';
            if (method_exists($this, $method)) {
                $this->$method();
            }
        }
    }
    
    // 寫入快取檔案
    protected function write($key, $content)
    {
        if (!in_array($key, $this->allowedKeys)) {
            throw new \Exception("非法快取鍵值: {$key}");
        }
        
        $file = $this->cacheDir . "cache_{$key}.php";
        $content = "<?php\n"
            . "// 自動生成快取檔案,請勿手動修改\n"
            . "// 建立時間: " . date('Y-m-d H:i:s') . "\n\n"
            . "if (!defined('IN_APP')) exit('Access Denied');\n\n"
            . $content . "\n?>";
        
        file_put_contents($file, $content);
        chmod($file, 0644);
    }
    
    // 讀取快取
    public function read($key)
    {
        $file = $this->cacheDir . "cache_{$key}.php";
        if (file_exists($file)) {
            return include $file;
        }
        return null;
    }
    
    // 具體快取生成範例:文章分類
    protected function class_refresh()
    {
        $db = \Database::getInstance();
        $rows = $db->query("SELECT id, name, description, sort, post_count FROM categories ORDER BY sort");
        
        $data = [];
        foreach ($rows as $row) {
            $data[$row['id']] = $row;
        }
        
        $this->write('class', '$class_cache = ' . var_export($data, true) . ';');
    }
}

2. 應用程式啟動時自動載入快取 common.php

<?php
$cache = new \Core\Cache();

// 優先載入系統設定(其他快取可能依賴設定值)
if (!$config = $cache->read('config')) {
    $cache->refresh('config');
    exit('系統設定快取已重建,請重新整理頁面');
}

// 批次載入其他快取
$missing = [];
foreach (['class', 'archive', 'newcomment', 'newpost', 'link', 'tag', 'statistic', 'topblog'] as $key) {
    if (!$cache->read($key)) {
        $missing[] = $key;
    }
}

if ($missing) {
    $cache->refresh(implode(',', $missing));
    exit('部分快取已重建,請重新整理頁面');
}

快取策略進階:多層級快取架構

層級儲存介質特性適用場景
L1: 請求級 PHP 陣列/靜態變數生命週期極短、零網路延遲同一請求內重複查詢(如多次獲取用戶資訊)
L2: 本地檔案PHP 序列化檔案/OPcache持久化、單機效能佳中小型專案、配置資料、模板編譯
L3: 分散式記憶體Redis / Memcached跨伺服器共享、高併發、支援 TTL/淘汰策略叢集部署、Session、熱門資料、排行榜
L4: 全頁靜態化HTML 檔案 / CDN完全繞過 PHP 執行、極致效能內容型網站、商品詳情頁、部落格文章

Redis 快取實作範例

<?php
namespace Core;

class RedisCache
{
    protected $redis;
    protected $prefix = 'app:cache:';
    protected $defaultTtl = 3600; // 1小時
    
    public function __construct($host = '127.0.0.1', $port = 6379)
    {
        $this->redis = new \Redis();
        $this->redis->connect($host, $port);
    }
    
    public function get($key)
    {
        $data = $this->redis->get($this->prefix . $key);
        return $data ? unserialize($data) : null;
    }
    
    public function set($key, $value, $ttl = null)
    {
        return $this->redis->setex(
            $this->prefix . $key, 
            $ttl ?? $this->defaultTtl, 
            serialize($value)
        );
    }
    
    public function delete($key)
    {
        return $this->redis->del($this->prefix . $key);
    }
    
    // 批次刪除模式匹配的鍵
    public function flushPattern($pattern)
    {
        $keys = $this->redis->keys($this->prefix . $pattern);
        if ($keys) {
            return $this->redis->del(...$keys);
        }
        return 0;
    }
    
    // 原子性計數器(適合統計、限流)
    public function increment($key, $ttl = 60)
    {
        $fullKey = $this->prefix . 'counter:' . $key;
        $count = $this->redis->incr($fullKey);
        if ($count === 1) {
            $this->redis->expire($fullKey, $ttl);
        }
        return $count;
    }
}

快取失效策略

  1. TTL 過期:設定存活時間,到期自動失效(簡單但可能讀到舊資料)
  2. 主動清除:資料更新時同步刪除/更新快取(強一致性,複雜度高)
  3. 版本號/標籤:快取鍵包含版本號,資料異動時遞增版本(平衡一致性與效能)
  4. 延雙刪:更新資料前後各刪除一次快取,配合延遲佇列重試(分散式系統常用)

常見陷阱與避坑指南

  • ❌ 快取擊穿:熱門鍵過期瞬間大量請求打入 DB → 互斥鎖 + 隨機 TTL
  • ❌ 快取穿透:查詢不存在資料每次都穿透 DB → 快取空值 + 布隆過濾器
  • ❌ 快取雪崩:大量快取同時間過期 → TTL 加隨機值 + 多級快取
  • ❌ 資料不一致:雙寫失敗 → 延雙刪 / Canal 監聽 Binlog 異步更新

總結

PHP 快取技術從簡單的檔案快取演進至多級分散式架構,核心在於根據業務場景選擇適合的策略。中小專案檔案快取足夠;高併發系統需引入 Redis 叢集並設計完善的失效機制。記住:快取是優化手段而非架構基石,優先優化 SQL、索引、架構設計,再考慮快取。

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