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

PHP 檔案快取類別實作:頁面快取、資料快取與 Memcached 整合

     編輯:PHP 綜合知識

PHP 快取分為頁面快取(輸出緩衝)、資料快取(變數/查詢結果)、記憶體快取。本文實作一個通用的檔案快取類別,並介紹 Memcached 記憶體快取整合方式。

一、通用檔案快取類別

<?php
/**
 * 檔案快取類別
 * 支援:頁面快取、變數快取、自動過期、目錄自動建立、快取清除
 */
class FileCache {
    private $cache_dir;
    private $default_ttl;
    private $extension = '.cache.php';

    public function __construct($cache_dir = './cache/', $default_ttl = 3600) {
        $this->cache_dir = rtrim($cache_dir, '/') . '/';
        $this->default_ttl = $default_ttl;
        $this->ensureDir($this->cache_dir);
    }

    // 取得快取檔案路徑 (MD5 雜湊避免檔名過長/特殊字元)
    private function getPath($key) {
        return $this->cache_dir . md5($key) . $this->extension;
    }

    // 確保目錄存在
    private function ensureDir($dir) {
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
    }

    // 寫入快取
    public function set($key, $data, $ttl = null) {
        $ttl = $ttl ?? $this->default_ttl;
        $path = $this->getPath($key);
        
        $content = "getPath($key);
        
        if (!file_exists($path)) return false;
        
        // 檢查過期 (讀取檔案前幾行判斷)
        $meta = $this->readMeta($path);
        if ($meta && $meta['expires'] < time()) {
            $this->delete($key);
            return false;
        }
        
        // 直接 include 回傳資料 (OPcache 可加速)
        $data = @include $path;
        return $data !== false ? $data : false;
    }

    // 讀取中繼資料 (不載入完整資料)
    private function readMeta($path) {
        $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $meta = ['created' => 0, 'expires' => 0];
        foreach ($lines as $line) {
            if (strpos($line, 'Created:') === 0) {
                $meta['created'] = (int)trim(str_replace('// Created:', '', $line));
            } elseif (strpos($line, 'Expires:') === 0) {
                $meta['expires'] = (int)trim(str_replace('// Expires:', '', $line));
            }
        }
        return $meta;
    }

    // 刪除快取
    public function delete($key) {
        $path = $this->getPath($key);
        if (file_exists($path)) {
            return unlink($path);
        }
        return false;
    }

    // 清除所有快取
    public function clear() {
        $files = glob($this->cache_dir . '*' . $this->extension);
        $count = 0;
        foreach ($files as $file) {
            if (is_file($file) && unlink($file)) $count++;
        }
        return $count;
    }

    // 取得快取資訊
    public function info($key) {
        $path = $this->getPath($key);
        if (!file_exists($path)) return false;
        $meta = $this->readMeta($path);
        $meta['size'] = filesize($path);
        $meta['path'] = $path;
        $meta['is_expired'] = $meta['expires'] < time();
        return $meta;
    }

    // 頁面快取輔助:開始捕捉輸出
    public function startPageCache($key) {
        $this->current_page_key = $key;
        ob_start();
    }

    // 頁面快取輔助:結束並儲存
    public function endPageCache($ttl = null) {
        $content = ob_get_clean();
        if ($content !== false) {
            $this->set($this->current_page_key, $content, $ttl);
        }
        echo $content;
    }

    // 頁面快取輔助:檢查並輸出 (適用於簡單頁面)
    public function checkPageCache($key) {
        $cached = $this->get($key);
        if ($cached !== false) {
            echo $cached;
            return true;  // 已輸出快取
        }
        return false;  // 需重新生成
    }
}
?>

二、使用範例

1. 變數/查詢結果快取

<?php
require 'FileCache.php';
$cache = new FileCache('./cache/', 1800);  // 30 分鐘

// 檢查快取
$cache_key = 'user_list_' . $page;
$users = $cache->get($cache_key);

if ($users === false) {
    // 快取未命中,查詢資料庫
    $stmt = $pdo->prepare('SELECT * FROM users LIMIT ?, 20');
    $stmt->execute([($page-1)*20]);
    $users = $stmt->fetchAll();
    
    // 寫入快取
    $cache->set($cache_key, $users);
}

foreach ($users as $user) {
    echo $user['name'] . '
'; } ?>

2. 頁面快取 (簡單版)

<?php
$cache = new FileCache('./cache/page/', 3600);
$page_key = 'page_' . md5($_SERVER['REQUEST_URI']);

// 檢查快取
if ($cache->checkPageCache($page_key)) {
    exit;  // 直接輸出快取內容結束
}

// 否則生成頁面...
?>
<html><body>
<h1>動態生成的頁面</h1>
<p>時間: <?= date('H:i:s') ?></p>
<?php
// 頁面結束前儲存快取
$cache->set($page_key, ob_get_contents());
?>
</body></html>

3. 頁面快取 (輸出緩衝版)

<?php
$cache = new FileCache('./cache/page/', 3600);
$page_key = 'page_' . md5($_SERVER['REQUEST_URI']);

// 檢查快取
if ($cache->checkPageCache($page_key)) {
    exit;
}

// 開始捕捉
$cache->startPageCache($page_key);
?>
<html><body>
<h1>動態頁面</h1>
<p>時間: <?= date('H:i:s') ?></p>
<?php
// 結束並自動儲存
$cache->endPageCache();
?>
</body></html>

三、Memcached 記憶體快取整合

# 安裝
apt-get install memcached php-memcached
# 或
pecl install memcached
<?php
/**
 * Memcached 快取包裝類別
 * 介面與 FileCache 相容,易於切換
 */
class MemCache {
    private $memcached;
    private $default_ttl;

    public function __construct($host = '127.0.0.1', $port = 11211, $default_ttl = 3600) {
        $this->memcached = new Memcached();
        $this->memcached->addServer($host, $port);
        $this->default_ttl = $default_ttl;
        
        // 可選:設定序列化器 (igbinary 較快)
        // $this->memcached->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_IGBINARY);
    }

    public function set($key, $data, $ttl = null) {
        $ttl = $ttl ?? $this->default_ttl;
        return $this->memcached->set($key, $data, $ttl);
    }

    public function get($key) {
        return $this->memcached->get($key);
    }

    public function delete($key) {
        return $this->memcached->delete($key);
    }

    public function clear() {
        return $this->memcached->flush();
    }

    public function getMulti($keys) {
        return $this->memcached->getMulti($keys);
    }

    public function setMulti($items, $ttl = null) {
        $ttl = $ttl ?? $this->default_ttl;
        return $this->memcached->setMulti($items, $ttl);
    }

    // 計數器 (原子操作)
    public function increment($key, $offset = 1, $initial = 0, $expiry = 0) {
        return $this->memcached->increment($key, $offset, $initial, $expiry);
    }

    public function decrement($key, $offset = 1, $initial = 0, $expiry = 0) {
        return $this->memcached->decrement($key, $offset, $initial, $expiry);
    }
}
?>

使用範例

<?php
$cache = new MemCache('127.0.0.1', 11211, 1800);

// 快取資料庫查詢
$key = 'hot_articles';
$articles = $cache->get($key);
if ($articles === false) {
    $articles = $pdo->query('SELECT * FROM articles WHERE views > 1000 ORDER BY views DESC LIMIT 10')->fetchAll();
    $cache->set($key, $articles);
}

// 計數器:文章瀏覽數
$article_id = 123;
$views = $cache->increment("article_views_{$article_id}", 1, 0, 86400);  // 24小時過期
echo "瀏覽數: $views";

// 批次操作
$keys = ['user_1', 'user_2', 'user_3'];
$users = $cache->getMulti($keys);
$missing = array_diff_key(array_flip($keys), $users);
if ($missing) {
    // 查詢資料庫補齊...
    $cache->setMulti($new_users);
}
?>

四、快取策略比較

策略適用場景優點缺點
檔案快取中小型專案、靜態頁面、查詢結果零相依、OPcache 加速、持久化IO 瓶頸、分散式需共享儲存
Memcached高併發、分散式、Session、計數器記憶體級速度、原子操作、叢集資料不持久、無備份、記憶體有限
Redis複雜資料結構、發布訂閱、排行榜豐富資料型別、持久化選項、Lua 腳本單執行緒、記憶體限制
APCu單機、Opcode 快取、小資料極快、零網路延遲、共享記憶體單機限制、重啟失效、無分散式

五、多層快取架構建議

<?php
/**
 * 多層快取:L1 (APCu/記憶體) -> L2 (Redis/Memcached) -> L3 (檔案/資料庫)
 */
class MultiLevelCache {
    private $l1;  // APCu
    private $l2;  // Redis/Memcached
    private $l3;  // FileCache

    public function __construct() {
        if (extension_loaded('apcu')) $this->l1 = new APCuCache();
        $this->l2 = new MemCache();  // 或 Redis
        $this->l3 = new FileCache();
    }

    public function get($key) {
        // L1
        if ($this->l1 && ($val = $this->l1->get($key)) !== false) return $val;
        // L2
        if (($val = $this->l2->get($key)) !== false) {
            $this->l1 && $this->l1->set($key, $val, 60);  // 回填 L1
            return $val;
        }
        // L3
        if (($val = $this->l3->get($key)) !== false) {
            $this->l2->set($key, $val);
            $this->l1 && $this->l1->set($key, $val, 60);
            return $val;
        }
        return false;
    }

    public function set($key, $data, $ttl = 3600) {
        $this->l1 && $this->l1->set($key, $data, min($ttl, 300));
        $this->l2->set($key, $data, $ttl);
        $this->l3->set($key, $data, $ttl);
    }

    public function delete($key) {
        $this->l1 && $this->l1->delete($key);
        $this->l2->delete($key);
        $this->l3->delete($key);
    }
}
?>

選擇快取策略取決於專案規模、架構與資料特性。中小專案檔案快取足夠;高併發需 Memcached/Redis;極致效能可疊加多層快取。

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