天天看點

Memcache時模拟命名空間及緩存失效問題的解決

在用memcache , 不可否認,memcache 在實際應用中的表現非常出色。不過也許正是因為其對速度和性能的要求過于嚴格,導緻 memcache 的可操作性不是那麼強。php 在使用的時候,也隻能有限的使用 add、delete、replace、flush 等幾個簡單的方法。

memcache 官方是不建議我們手動去删除緩沖内容的, 因為官方的 api 中有關删除的方法隻有 delete 和flush,前者是在知道 key 的情況下删除一個對象的值,後面是清除伺服器上所有對象。

不過很多情況下,我們需要從伺服器上删除一部分對象,又不希望把整個伺服器上的對象都清空,官方的 faq 上提供了一個“僞命名空間(simulating namespaces )” 的作法。

Memcache時模拟命名空間及緩存失效問題的解決

<?php  

//生成一個用來儲存 namespace 的 key  

$ns_key = $memcache->get("product_namespace_key");  

//如果 key 不存在,則建立  

if ($ns_key === false) $memcache->set("product_namespace_key", time());  

//根據 namespace_key 生成真正的 key  

$my_key = $ns_key . md5($sql);  

// 擷取目前key下的緩存  

$val = $memcache->get($my_key);  

if (!$val) {  

    $memcache->set($my_key, $dbvalue, 600);  

}  

//需要删除整個namespace裡的對象的時候,這樣處理即可:  

$memcache->increment("product_namespace_key");  

?>  

memcache緩存失效問題

在大并發的場合,當cache失效時,大量并發同時取不到cache,會同一瞬間去通路db并回設cache,可能會給系統帶來潛在的超負荷風險。解決方法:

方法一 

在load db之前先add一個mutex key, mutex key add成功之後再去做加載db, 如果add失敗則sleep之後重試讀取原cache資料。為了防止死鎖,mutex key也需要設定過期時間。僞代碼如下

Memcache時模拟命名空間及緩存失效問題的解決

if (memcache.get(key) == null) {  

    // 3 min timeout to avoid mutex holder crash  

    if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  

        value = db.get(key);  

        memcache.set(key, value);  

        memcache.delete(key_mutex);  

    } else {  

        sleep(50);  

        retry();  

    }  

方法二 

Memcache時模拟命名空間及緩存失效問題的解決

v = memcache.get(key);  

if (v == null) { //不存在  

} else {  

    if (v.timeout <= now()) {  //過期  

        if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  

            // extend the timeout for other threads  

            v.timeout += 3 * 60 * 1000;  

            memcache.set(key, v, key_timeout * 2);  

            // load the latest value from db  

            v = db.get(key);  

            v.timeout = key_timeout;  

            memcache.set(key, value, key_timeout * 2);  

            memcache.delete(key_mutex);  

        } else {  

            sleep(50);  

            retry();  

        }  

}