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

PHP 頁面緩存技術全景:從 OB 函數到文件緩存類的完整演進

     編輯:PHP 相關知識

頁面緩存是提升 PHP 應用吞吐量最直接的手段。本文系統梳理 ob_start 系列函數原理、手工文件緩存模式、帶過期時間的緩存類、以及生產級緩存策略,助你從「會用」進階到「懂原理、能選型」。

一、OB 緩存機制原理

PHP 輸出緩存(Output Buffering)在腳本與 SAPI(如 Apache、Nginx+PHP-FPM)之間插入一層緩存區:

ob_start();           // 開啟緩存,後續 echo/print/HTML 先進緩存區
$content = ob_get_contents(); // 讀取緩存區內容(字串)
ob_end_flush();       // 發送緩存區內容到瀏覽器並關閉緩存
ob_end_clean();       // 僅丟棄緩存區內容,不輸出

嵌套特性:ob_start() 可多次調用,形成棧結構,ob_get_level() 返回當前層級。

二、基礎文件緩存模式(手工版)

<?php
$cacheDir = __DIR__ . '/cache/pages/';
$cacheFile = $cacheDir . md5($_SERVER['REQUEST_URI']) . '.html';
$cacheTime = 3600; // 秒

// 1. 檢查緩存是否有效
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
    readfile($cacheFile);
    exit;
}

// 2. 無緩存或過期:開啟 OB 捕獲輸出
ob_start();
// ... 你的業務邏輯、模板渲染、echo 輸出 ...
$content = ob_get_contents();
ob_end_flush(); // 輸出給用戶

// 3. 寫入緩存文件
if (!is_dir($cacheDir)) mkdir($cacheDir, 0755, true);
file_put_contents($cacheFile, $content, LOCK_EX);
?>

三、進階:帶過期時間頭部的緩存類(生產就緒)

<?php
class PageCache {
    private string $cacheDir;
    private int $ttl;
    private string $cacheFile;

    public function __construct(string $cacheDir, int $ttl = 3600) {
        $this->cacheDir = rtrim($cacheDir, '/') . '/';
        $this->ttl = $ttl;
        $this->cacheFile = $this->cacheDir . sha1($_SERVER['REQUEST_URI']) . '.html';
    }

    public function start(): bool {
        if (!$this->isValid()) {
            ob_start();
            return false; // 需要重新生成
        }
        // 緩存有效:直接輸出並結束
        readfile($this->cacheFile);
        exit;
    }

    public function end(): void {
        $content = ob_get_contents();
        ob_end_flush();
        $this->save($content);
    }

    private function isValid(): bool {
        if (!file_exists($this->cacheFile)) return false;
        $meta = unserialize(file_get_contents($this->cacheFile . '.meta'));
        return $meta['expire'] > time();
    }

    private function save(string $content): void {
        if (!is_dir($this->cacheDir)) mkdir($this->cacheDir, 0755, true);
        $meta = ['expire' => time() + $this->ttl, 'created' => time()];
        file_put_contents($this->cacheFile . '.meta', serialize($meta), LOCK_EX);
        file_put_contents($this->cacheFile, $content, LOCK_EX);
    }
}

// 使用
$cache = new PageCache('/var/www/cache', 600);
if (!$cache->start()) {
    // 業務邏輯
    echo "動態生成內容 " . date('H:i:s');
    $cache->end();
}

設計亮點:

  • 元數據分離(.meta 文件)避免解析大文件獲取過期時間。
  • LOCK_EX 防併發寫入損壞。
  • Constructor 注入依賴,易於測試與多實例(不同 TTL)。

四、緩存失效策略:主動清理 vs 被動過期

策略適用場景實現方式
TTL 過期內容變化頻率可預估(如首頁 10 分鐘)文件 mtime 或 .meta expire
標籤/關鍵字失效文章更新需清理相關列表頁、詳情頁Redis 集合存儲 tag:article:123 → [cache_key1, cache_key2]
版本號/部署失效代碼發布後全站緩存重建緩存 key 前綴加 v{git_commit_hash}
手動 API 清理後台編輯發布文章時觸發HTTP 端點 POST /cache/purge {keys:[]}

五、高併發下的「緩存擊穿/穿透/雪崩」防禦

  • 擊穿:熱點 key 過期瞬間大量請求打穿 DB。→ mutex lock(flock/Redis SETNX)單請求重建,其餘等待/返回舊值。
  • 穿透:查詢不存在的 key(惡意 ID)。→ 布隆過濾器攔截 / 緩存空值(短 TTL)。
  • 雪崩:大量 key 同一時間過期。→ TTL 加隨機抖動($ttl + rand(0, 300))。

六、何時不應使用頁面級緩存

  1. 高度個性化頁面(用戶專屬推薦、購物車、已登入狀態)。
  2. 頻繁寫入、實時性要求極高(股票行情、即時聊天)。
  3. 非 GET 請求(POST/PUT/DELETE 不緩存)。

此類場景改用片段緩存(Redis::get('user:123:profile'))或數據緩存(查詢結果緩存)。

七、總結與選型建議

方案複雜度性能適用階段
OB + 文件低中(受限於磁盤 IO)中小流量、快速上線
OB + Redis/Memcached中高(記憶體級)高併發、分佈式
Nginx fastcgi_cache / proxy_cache低(運維層)極高(內核態)靜態化程度高、無動態個性化
Varnish / CDN Edge高(架構層)極高全站加速、多節點

從「OB 文件緩存」起步,隨業務增長平滑遷移至「Redis 片段緩存」→「Nginx 微緩存」→「CDN 全站緩存」,是 PHP 架構演進的經典路徑。

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