三大主流文件緩存模式
| 模式 | 原理 | 優點 | 缺點 | 適用場景 |
| PHP 代碼導出 | var_export/陣列語法寫入 .php,include 載入 | OPcache 自動優化、讀取極快、可見性好 | 寫入慢、大數據文件大、無法存資源/閉包 | 配置表、字典表、靜態資源映射 |
| 序列化存儲 | serialize() 寫入 .dat/.cache,unserialize() 還原 | 支援任意結構、體積較小、寫入快 | 反序列化有開銷、版本兼容風險、安全隱患 | 會話、複雜物件、臨時緩存 |
| JSON 存儲 | json_encode() 寫入 .json,json_decode() 還原 | 跨語言、人類可讀、安全、流式解析 | 無法存資源/類、大數組解析慢、無註釋 | API 響應緩存、前端同構、配置文件 |
方案一:PHP 陣列導出類(原文 DataCache)
<?php
class PhpArrayCache {
public static function write(string $file, string $varName, array $data): bool {
$code = " $v) {
$key = is_int($k) ? $k : var_export($k, true);
$val = self::exportValue($v, $level + 1);
$parts[] = "$nextIndent$key => $val";
}
return "$varName = [\n" . implode(",\n", $parts) . "\n$indent];";
}
private static function exportValue($v, int $level): string {
if (is_array($v)) return self::varExport($v, '', $level);
if (is_string($v)) return var_export($v, true);
if (is_bool($v)) return $v ? 'true' : 'false';
if (is_null($v)) return 'null';
if (is_numeric($v)) return (string)$v;
// 物件/資源/閉包無法導出
throw new \Exception("Unsupported type: " . gettype($v));
}
}
方案二:序列化文件緩存類(原文 Cache_FileCache)
<?php
class SerializeFileCache {
private string $dir;
private int $defaultTtl;
public function __construct(string $dir, int $defaultTtl = 3600) {
$this->dir = rtrim($dir, '/');
$this->defaultTtl = $defaultTtl;
if (!is_dir($this->dir)) mkdir($this->dir, 0755, true);
}
public function set(string $key, $data, int $ttl = 0): bool {
$ttl = $ttl ?: $this->defaultTtl;
$file = $this->path($key);
$payload = [
'data' => $data,
'expire' => $ttl ? time() + $ttl : 0,
'mtime' => time(),
];
$content = serialize($payload);
return $this->atomicWrite($file, $content);
}
public function get(string $key) {
$file = $this->path($key);
if (!is_file($file)) return null;
$payload = @unserialize(file_get_contents($file));
if (!$payload) return $this->delete($key) ?: null;
if ($payload['expire'] && $payload['expire'] < time()) {
return $this->delete($key) ?: null;
}
return $payload['data'];
}
public function delete(string $key): bool {
return @unlink($this->path($key));
}
public function clear(): void {
foreach (glob($this->dir . '/*.cache') as $f) @unlink($f);
}
private function path(string $key): string {
return $this->dir . '/' . md5($key) . '.cache';
}
private function atomicWrite(string $file, string $content): bool {
$tmp = $file . '.tmp.' . random_bytes(4);
if (file_put_contents($tmp, $content, LOCK_EX) === false) return false;
return rename($tmp, $file);
}
}
方案三:Memcached 分散式緩存(生產首選)
<?php
class MemcachedCache {
private \Memcached $mc;
public function __construct(array $servers = [['127.0.0.1', 11211]]) {
$this->mc = new \Memcached();
$this->mc->addServers($servers);
// 關鍵優化配置
$this->mc->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$this->mc->setOption(\Memcached::OPT_TCP_NODELAY, true);
$this->mc->setOption(\Memcached::OPT_NO_BLOCK, true);
$this->mc->setOption(\Memcached::OPT_COMPRESSION, true);
$this->mc->setOption(\Memcached::OPT_COMPRESSION_TYPE, \Memcached::COMPRESSION_FASTLZ);
$this->mc->setOption(\Memcached::OPT_SERIALIZER, \Memcached::SERIALIZER_IGBINARY); // 需安裝 igbinary
}
public function get(string $key) {
return $this->mc->get($key);
}
public function set(string $key, $value, int $ttl = 3600): bool {
return $this->mc->set($key, $value, $ttl);
}
public function delete(string $key): bool {
return $this->mc->delete($key);
}
public function getMulti(array $keys): array {
return $this->mc->getMulti($keys);
}
public function flush(): bool {
return $this->mc->flush();
}
}
統一抽象層:PSR-16 Simple Cache 實作
<?php
interface CacheInterface extends \Psr\SimpleCache\CacheInterface {}
class UnifiedCache implements CacheInterface {
private CacheInterface $driver;
public function __construct(string $driver = 'memcached', array $config = []) {
$this->driver = match ($driver) {
'memcached' => new MemcachedCache($config['servers'] ?? []),
'redis' => new RedisCache($config),
'file' => new SerializeFileCache($config['dir'] ?? '/tmp/cache'),
'array' => new PhpArrayCache(),
default => throw new \InvalidArgumentException("Unknown driver: $driver"),
};
}
// 代理所有方法...
public function get($key, $default = null) { return $this->driver->get($key) ?? $default; }
public function set($key, $value, $ttl = null) { return $this->driver->set($key, $value, $ttl ?? 3600); }
public function delete($key) { return $this->driver->delete($key); }
public function clear() { return $this->driver->clear(); }
public function getMultiple($keys, $default = null) { /* ... */ }
public function setMultiple($values, $ttl = null) { /* ... */ }
public function deleteMultiple($keys) { /* ... */ }
public function has($key) { return $this->driver->get($key) !== null; }
}
選型決策樹
需要跨進程/跨機器共享?
├─ 是 → 需要亞毫秒延遲?
│ ├─ 是 → Memcached (純記憶體、多線程、簡單)
│ └─ 否 → Redis (持久化、豐富資料結構、Lua 腳本)
└─ 否 → 單機、低併發、配置型數據?
├─ 需要 OPcache 加速、可讀性強 → PHP 陣列導出
├─ 需要存儲複雜物件、寫入頻繁 → 序列化文件
└─ 需要跨語言、調試友好 → JSON 文件
避坑指南
| 坑點 | 後果 | 防範 |
| 文件緩存無鎖併發寫入 | 文件損壞、數據丟失 | 臨時文件 + rename 原子寫入、flock() |
| 序列化存儲類定義變更 | 反序列化失敗/行為異常 | 實作 Serializable、版本號、避免緩存實體類 |
| Memcached 大鍵(>1MB) | 網路阻塞、超時、驅逐 | 拆分鍵、壓縮、改用 Redis Hash/Stream |
| 緩存穿透/雪崩/擊穿 | DB 壓垮、服務不可用 | 空值緩存、隨機 TTL、互斥鎖重建 |
| 未監控命中率/記憶體/驅逐 | 無感知效能退化 | Prometheus + Grafana 監控關鍵指標 |
文件緩存適合單機、低頻、結構固定的場景;分散式系統的核心緩存層必須使用 Memcached/Redis。建立統一的 CacheInterface 抽象,讓業務代碼零成本遷移、測試雙模式(陣列驅動測試、Redis 驅動生產),是現代 PHP 架構的基本功。