首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP Memcache 命名空間模擬與緩存失效穿透解決方案

     編輯:PHP 綜合知識

Memcache 原生不支援命名空間,但在大型應用中常需批次清除特定群組的快取。本文介紹利用版本號機制模擬命名空間,並針對高併發下的快取失效穿透問題,提供兩種經典解決策略。

一、模擬命名空間機制

核心概念:為每個邏輯群組建立一個「版本號鍵」,所有該群組的快取鍵皆包含此版本號。需清除群組時,僅需更新版本號,舊快取鍵自然失效,配合 Memcache LRU 機制自動淘汰。

程式碼實作

class Action {
    public function index() {
        global $mc_wr;
        
        // 取得或初始化命名空間版本號
        $ns_key = $mc_wr->get("foo_namespace_key");
        if ($ns_key === false) {
            $mc_wr->set("foo_namespace_key", time());
            $ns_key = time();
        }
        
        // 組合帶版本號的唯一快取鍵
        $otherParms = 'select * from user LIMIT 1';
        $my_key = "foo_{$ns_key}_" . md5($otherParms);
        
        // 讀取快取
        $val = $mc_wr->get($my_key);
        if (!$val) {
            $value = 'wangdekang_' . time();
            // 設定 600 秒過期,並加入隨機秒數防止雪崩
            $mc_wr->set($my_key, $value, 600, rand(0, 60));
        }
        echo $val;
    }

    public function clear_ns() {
        global $mc_wr;
        // 更新版本號,令該命名空間下所有快取瞬間失效
        $mc_wr->set('foo_namespace_key', time());
    }
}

二、快取失效穿透問題與解決

高併發場景下,熱門快取過期瞬間,大量請求同時穿透至資料庫,造成系統過載。以下提供兩種互斥鎖策略:

方法一:簡單互斥鎖

快取未命中時,嘗試寫入短效互斥鍵(如 3 分鐘)。成功者負責回源資料庫並更新快取;失敗者短暫休眠後重試讀取快取。

if ($memcache->get($key) === null) {
    // 3 分鐘互斥鍵過期,防止持有者異常導致死鎖
    if ($memcache->add($key_mutex, 1, 180)) {
        $value = $db->get($key);
        $memcache->set($key, $value);
        $memcache->delete($key_mutex);
    } else {
        usleep(50000); // 50ms
        // 重試邏輯...
    }
}

方法二:雙層過期時間(進階)

在快取值內部嵌入 timeout1(邏輯過期),設定比 Memcache 實際過期 timeout2 更短。讀取時若發現 timeout1 已過期,先延長 timeout1 並寫回快取,再回源資料庫更新真實資料。此法可將「穿透窗口」縮減至單一請求。

$v = $memcache->get($key);
if ($v === null) {
    // 同方法一:互斥鎖回源
} else {
    if ($v['timeout'] <= time()) {
        // 邏輯過期,搶鎖延長並回源
        if ($memcache->add($key_mutex, 1, 180)) {
            $v['timeout'] += 180; // 延長邏輯過期
            $memcache->set($key, $v, $real_timeout * 2); // 寫回延長後的快取
            
            $fresh = $db->get($key); // 回源
            $fresh['timeout'] = $real_timeout;
            $memcache->set($key, $fresh, $real_timeout * 2); // 寫入新資料
            $memcache->delete($key_mutex);
        } else {
            usleep(50000);
            // 重試...
        }
    }
}

關鍵優化重點

  • 隨機過期時間:set() 最後一個參數加入隨機值,避免大量快取同一秒失效引發雪崩
  • 互斥鍵過期保護:必須設定互斥鍵 TTL,防止持有者崩潰導致永久阻塞
  • 雙層過期:方法二雖複雜,但能將資料庫壓力降至最低,適合極高併發核心業務

實務上建議根據業務併發量選擇:中小流量採方法一即可;核心高併發介面建議實作方法二,並搭配監控告警。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved