針對 Memcache/Redis 封裝 Cache 類別時常見問題:重複的「查快取→無則查 DB→存快取」邏輯、Key 組裝分散、過期時間分散、高併發快取失效導致 Dogpile 效應。本文提出統一解決方案:配置檔驅動 Key 組裝與過期時間、高效能回調機制、Redis TTL+SETNX 自動觸發更新。
'test' => [
'keys' => [
'good' => [
'timeout' => 600,
'params' => ['epid'=>1, 'num'=>1],
'desc' => '描述'
],
'top_test' => [
'timeout' => 60,
'ttl' => 10, // 自動觸發閾值(秒)
'params' => ['site_id'=>1, 'boutique'=>1, ...],
'desc' => '描述', 'author' => 'ugg'
]
]
]
Key 組裝演算法:namespace_key_param1=val1¶m2=val2...,如 site_top_feeds_site_id=12&boutique=1...。
測試顯示 call_user_func 比原生呼叫慢 3 倍,call_user_func_array 慢 5 倍。採用參數數量分派的直接呼叫:
switch (count($params)) {
case 0: $result = $obj->{$func}(); break;
case 1: $result = $obj->{$func}($params[0]); break;
// ... 最多 7 參數直接展開
default: $result = call_user_func_array([$obj, $func], $params);
}
效能僅比原生慢 1.25 倍,大幅優於內建函式。
public function smart_fetch($key, $callback, $namespace='wk') {
$fullKey = $prefix . $key . $suffix;
$result = $this->_redis->get($fullKey);
$bttl = false;
// TTL 自動觸發檢查
if (!empty($ttlConfig)) {
$rttl = $this->_redis->ttl($fullKey);
if ($rttl > 0 && $ttlConfig >= $rttl && $this->_redis->setnx("lock:$fullKey", true)) {
$this->_redis->expire("lock:$fullKey", 3); // 鎖 3 秒
$bttl = true;
}
}
// 快取未命中或觸發更新
if ($bttl || !$result || $flushFlag) {
$callback['params'] = array_values($params); // 重組參數
$result = Wk_Common::callback($callback); // 執行回調查 DB
$this->_redis->setex($fullKey, $expire, $result);
$result = $this->_redis->get($fullKey);
}
if ($bttl) $this->_redis->del("lock:$fullKey");
return $result;
}
TTL:取得剩餘過期秒數,快取即將失效前(剩餘 ≤ 閾值)觸發更新。SETNX:分散式鎖,只有一個進程搶到鎖執行 DB 查詢與快取更新。此方案整合配置驅動、高效能回調、自動觸發更新,徹底解決快取層常見五大痛點。