| 類型 | 存儲介質 | 讀寫速度 | 適用場景 |
| 文件緩存 | 磁盤 (SSD 推薦) | 中等 | 中小流量、靜態化頁面、配置緩存、無 Redis 環境 |
| 內存緩存 (Redis/Memcached) | RAM | 極快 | 高併發、分佈式、會話存儲、熱點數據 |
| Opcode 緩存 (OPcache) | 共享內存 | 極快 | PHP 代碼編譯緩存,生產環境必開 |
| HTTP 緩存 (ETag/Last-Modified) | 客戶端/代理 | 最快(無請求) | 靜態資源、REST API、CDN 配合 |
文件緩存類 Cache 完整實現
<?php
class Cache {
private $dir;
private $lifetime;
private $cacheId;
private $ext = '.cache.php';
public function __construct($dir = '', $lifetime = 1800) {
$this->dir = $dir ?: sys_get_temp_dir() . '/php_cache/';
$this->lifetime = $lifetime;
$this->initDir();
$this->cacheId = $this->getCacheId();
}
private function initDir() {
if (!is_dir($this->dir)) {
if (!mkdir($this->dir, 0755, true)) {
throw new RuntimeException("無法創建緩存目錄: {$this->dir}");
}
}
// 安全防護:禁止直接訪問緩存文件
$htaccess = $this->dir . '.htaccess';
if (!file_exists($htaccess)) {
file_put_contents($htaccess, "Deny from all\n");
}
}
private function getCacheId() {
$url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
return $this->dir . md5($url) . $this->ext;
}
// 判斷緩存是否有效
public function isValid() {
if (!file_exists($this->cacheId)) return false;
$mtime = filemtime($this->cacheId);
return (time() - $mtime) < $this->lifetime;
}
// 加載緩存(有效則輸出並終止腳本)
public function load() {
if ($this->isValid()) {
// 發送緩存標識頭(便於調試)
header('X-Cache: HIT');
include $this->cacheId;
exit;
}
// 無效則開啟輸出緩衝準備生成新緩存
header('X-Cache: MISS');
ob_start();
}
// 寫入緩存
public function write($content = '') {
$content = $content ?: ob_get_contents();
ob_end_flush();
$tmpFile = $this->cacheId . '.tmp';
if (file_put_contents($tmpFile, $content) === false) {
throw new RuntimeException("緩存寫入失敗: {$tmpFile}");
}
// 原子重命名避免讀到半寫文件
rename($tmpFile, $this->cacheId);
}
// 清除單個緩存
public function clean() {
if (file_exists($this->cacheId)) unlink($this->cacheId);
}
// 清除所有緩存
public function cleanAll() {
$files = glob($this->dir . '*' . $this->ext);
foreach ($files as $f) unlink($f);
}
// 獲取緩存信息
public function getInfo() {
return [
'file' => $this->cacheId,
'exists' => file_exists($this->cacheId),
'size' => file_exists($this->cacheId) ? filesize($this->cacheId) : 0,
'mtime' => file_exists($this->cacheId) ? date('Y-m-d H:i:s', filemtime($this->cacheId)) : null,
'lifetime' => $this->lifetime,
];
}
}
使用方法:極簡三步
<?php
// 1. 頁面最頂部引入並初始化
require_once 'Cache.php';
$cache = new Cache('./cache/', 300); // 5 分鐘緩存
// 2. 嘗試加載緩存(命中直接輸出並 exit)
$cache->load();
// 3. 你的業務邏輯代碼(查詢數據庫、渲染模板等)
echo "<h1>動態生成的內容</h1>";
echo "當前時間: " . date('H:i:s');
$users = $db->query('SELECT * FROM users LIMIT 10');
foreach ($users as $u) echo $u['name'] . '<br>';
// 4. 頁面底部寫入緩存
$cache->write();
進階用法
手動刷新緩存
// URL 帶參數強制刷新:page.php?cache_act=refresh
if (isset($_GET['cache_act']) && $_GET['cache_act'] === 'refresh') {
$cache->clean();
// 可選:重定向去除參數避免循環刷新
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
exit;
}
差異化緩存(按用戶/設備/語言)
// 修改 getCacheId() 包含更多維度
private function getCacheId() {
$keyParts = [
$_SERVER['REQUEST_URI'],
$_SESSION['user_id'] ?? 'guest',
$_COOKIE['lang'] ?? 'zh',
isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Mobile') ? 'mobile' : 'desktop',
];
return $this->dir . md5(implode('|', $keyParts)) . $this->ext;
}
緩存標籤/分組清理(需維護索引文件)
// 寫入時記錄標籤
$tags = ['user_list', 'homepage'];
$indexFile = $this->dir . '_tags.index.php';
$index = file_exists($indexFile) ? include $indexFile : [];
foreach ($tags as $tag) $index[$tag][] = $this->cacheId;
file_put_contents($indexFile, 'dir . '_tags.index.php';
if (!file_exists($indexFile)) return;
$index = include $indexFile;
if (isset($index[$tag])) {
foreach ($index[$tag] as $file) @unlink($file);
unset($index[$tag]);
file_put_contents($indexFile, '
性能優化建議
- SSD 盤:文件緩存目錄掛載在 SSD/NVMe 上
- tmpfs 內存盤:
mount -t tmpfs -o size=512m tmpfs /var/www/cache 將緩存目錄掛載到內存
- Nginx fastcgi_cache:在 Nginx 層面直接緩存 FastCGI 響應,繞過 PHP 執行,性能提升 10x+
- 預熱機制:部署後腳本自動請求熱門頁面生成緩存
- 監控指標:命中率、平均響應時間、緩存目錄大小、inode 使用率
避坑指南
- ❌ 緩存用戶敏感信息(賬單、個人中心)
- ❌ 緩存目錄可通過 Web 直接訪問(必須 .htaccess / Nginx 禁止)
- ❌ 緩存Key 衝突導致數據錯亂(包含完整 URI + 區分維度)
- ❌ 無緩存清理策略導致磁盤佔滿(定時任務清理過期/定期全量清理)
- ✅ 緩存內容包含 CSRF Token 等動態數據時需佔位符替換
- ✅ 開發環境禁用緩存或極短過期時間
文件緩存是「無 Redis 環境」或「靜態化頁面」場景下的最佳選擇,代碼簡單、零依賴、易維護。高併發核心業務請上 Redis + Nginx 多級緩存架構。
- 上一篇文章: PHP 錯誤類型與調試方法完全指南:Notice/Warning/Error 區別與三大排查技巧
- 下一篇文章: PHP AJAX 統一後端調度架構(cnblogs 版):反射機制實現動態路由完整代碼
Copyright ©
程式碼之家 All Rights Reserved