快取本質是「用空間換時間」:將「經常讀取、較少變動、生成成本高」的資料預先計算並儲存,後續請求直接讀取快取,避免重複資料庫查詢或複雜運算。
某部落格系統透過快取將單頁資料庫查詢從 15+ 次降至 3 次,快取項目包括:
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) . ';');
}
}
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 執行、極致效能 | 內容型網站、商品詳情頁、部落格文章 |
<?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;
}
}
PHP 快取技術從簡單的檔案快取演進至多級分散式架構,核心在於根據業務場景選擇適合的策略。中小專案檔案快取足夠;高併發系統需引入 Redis 叢集並設計完善的失效機制。記住:快取是優化手段而非架構基石,優先優化 SQL、索引、架構設計,再考慮快取。