傳統檔案快取 (`require 'cache/config.php'`) 存在 I/O 開銷、難以分散式部署、併發讀取鎖競爭等問題。**檔案快取轉記憶體快取** 將熱門資料遷移至 Memcached/Redis,實現微秒級讀取、跨節點共享、彈性擴展。
舊代碼遍佈 `require 'cache/xxx.php'`,需統一入口:
<?php
// 替換所有 require 'cache/xxx.php' 為:
require getCachePath('cache/xxx.php');
function getCachePath($file) {
$key = 'filecache:' . md5($file);
$data = Memcached::getInstance()->get($key);
if ($data !== false) {
// 註冊全域變數
registerGlobals($data);
return '__MEMORY_CACHE__'; // 佔位,實際不需載入檔案
}
// 降級讀取檔案
$path = __DIR__ . '/' . $file;
if (file_exists($path)) {
$data = include $path; // 假設檔案回傳陣列
Memcached::getInstance()->set($key, $data, 3600);
registerGlobals($data);
}
return $path;
}
?>
| 策略 | 程式碼 | 優點 | 缺點 |
|---|---|---|---|
| 迴圈註冊 | `foreach($vars as $k=>$v) $GLOBALS[$k]=$v;` | 覆蓋舊值、邏輯直觀 | 大陣列效能較差 |
| 陣列合併 | `$GLOBALS += $vars;` | 無迴圈、極快 | 鍵名衝突時保留舊值、不更新 |
| 選擇性合併 | `$GLOBALS = array_merge($GLOBALS, $vars);` | 新值覆蓋舊值 | 重新索引數字鍵、效能中等 |
建議:迴圈註冊 最安全,配合 `isset($GLOBALS[$k])` 判斷是否覆蓋,或依業務定義優先序。
<?php
class FileCacheToMemory {
private $backend; // 'memcached' | 'redis'
private $client;
private $prefix = 'fc2m:';
private $ttl = 3600;
public function __construct($backend = 'memcached', array $servers = []) {
$this->backend = $backend;
if ($backend === 'memcached') {
$this->client = new Memcached();
$this->client->addServers($servers);
} elseif ($backend === 'redis') {
$this->client = new Redis();
$this->client->connect($servers[0]['host'] ?? '127.0.0.1', $servers[0]['port'] ?? 6379);
}
}
public function load($file) {
$key = $this->prefix . md5($file);
$data = $this->getFromMemory($key);
if ($data !== false) return $this->register($data);
$path = __DIR__ . '/../cache/' . $file;
if (!file_exists($path)) return false;
$data = include $path; // 檔案需 return 陣列
if (!is_array($data)) return false;
$this->saveToMemory($key, $data);
return $this->register($data);
}
private function getFromMemory($key) {
return $this->backend === 'memcached'
? $this->client->get($key)
: $this->client->get($key);
}
private function saveToMemory($key, $data) {
if ($this->backend === 'memcached') {
$this->client->set($key, $data, $this->ttl);
} else {
$this->client->setex($key, $this->ttl, serialize($data));
}
}
private function register(array $data) {
foreach ($data as $k => $v) {
$GLOBALS['cache_' . $k] = $v; // 加前綴避免衝突
}
return $data;
}
}
// 使用
$cache = new FileCacheToMemory('redis', [['host'=>'127.0.0.1', 'port'=>6379]]);
$config = $cache->load('config.php'); // 同時註冊 $GLOBALS['cache_config']
?>
檔案快取轉記憶體快取的核心在於「統一路徑調度」與「安全的全域變數註冊」。透過封裝 `FileCacheToMemory` 類別,可平滑遷移既有專案,**但長遠來看,新專案應直接採用 Redis/Memcached 為主快取層**,配合 Cache-Aside、Read-Through、Write-Behind 等模式,構建高效能、高可用的快取架構。