2012-06-05 38 views
1

Memcached的是一個偉大的可擴展緩存層但它有一個很大的問題(我)它不能管理標籤。標籤對組合失效非常有用。的Memcache標籤仿真

我已經做了一些研究,我所知道的一些解決方案:(REF Best way to invalidate a number of memcache keys using standard php libraries?

我的一個最喜歡的解決方案是名稱空間,此解決方案在memcached wiki上進行了解釋。

但是我不明白爲什麼我們要將命名空間集成到鍵緩存上?

從我理解關於命名空間的訣竅是:要生成密鑰,我們必須獲取命名空間的值(在緩存上)。如果namespace->value緩存條目被逐出,我們不能再計算獲取緩存的好鑰匙......所以這個命名空間的緩存實際上是 invalidate(我之所以這麼說,是因爲緩存依然存在,但我們不能再計算關鍵訪問)。

那麼,爲什麼我們不能簡單地實現這樣的:

tag1->[key1, key2, key5] 
tag2->[key1, key3, key6] 
key1->["value" => value1, "tags" => [tag1, tag2]] 
key2->["value" => value2, "tags" => [tag1]] 
key3->["value" => value3, "tags" => [tag3]] 
etc... 

有了這個實現我回來的問題,如果tag1->[key1, key2, key5]被逐出我們沒有更多的無效標記1鍵。但隨着

function load($cacheId) { 
    $cache = $memcache->get($cacheId); 
    if (is_array($cache)) { 
     $evicted = false; 
     // Check is no tags have been evicted 
     foreach ($cache["tags"] as $tagId) { 
     if (!$memcache->get($tagId) { 
      $evicted = true; 
      break; 
     } 
     } 
     // If no tags have been evicted we can return cache 
     if (!$evicted) { 
     return $cache 
     } else { 
     // Not mandatory 
     $memcache->delete($cacheId); 
     } 
     // Else return false 
     return false; 
    } 
} 

這是僞代碼

我們一定會返回高速緩存,如果這一切標籤可用。

首先我們可以說它是「每次你需要緩存,我們必須檢查(/ get)X標籤,然後檢查數組」。但是對於命名空間,我們還必須檢查(/ get)命名空間以檢索命名空間值,主要差異是在數組之下迭代...但是我不認爲鍵會有很多標記(我無法想象超過10個標記/關鍵是我的應用程序),所以重複在大小10陣列這是相當速度..

所以我的問題是:有人已經想過這個實現?什麼是極限?我忘了什麼嗎?等

或者,也許我有missunderstand命名空間的概念...

PS:我不是在尋找另一個緩存層像memcached的標籤或Redis的

回答

1

我想你忘了什麼東西與此實現,但它是微不足道的修復。

考慮多個鍵分享一些標籤的問題:

key1 -> tag1 tag2 
key2 -> tag1 tag2 
tag1 -> key1 key2 
tag2 -> key1 key2 

說你加載名稱爲key1。仔細檢查tag1和tag2是否存在。這很好,而且是關鍵負載。

然後,tag1從緩存中以某種方式被逐出。

您的代碼會使tag1失效。這應該刪除key1和key2,但因爲tag1已被驅逐,所以不會發生。

然後你添加一個新的項目key3。它也指標籤1:

key3 -> tag1 

當保存這個關鍵,Tag1是(重新)創建:

tag1 -> key3 

後來從緩存中的僞代碼重新載入鍵1,當你檢查,以確保標籤1存在成功。並允許加載來自key1的(陳舊)數據。

很明顯,解決這個問題的方法是檢查tag1數據的值,以確保您加載的密鑰在該數組中列出,並且只有在密鑰有效時纔會考慮您的密鑰。

當然這可能會有性能問題取決於您的使用情況。如果給定的密鑰有10個標籤,但每個標籤都由10k個密鑰使用,那麼您必須通過10k個項目的數組搜索來找到您的密鑰,並在每次加載內容時重複這10次。

在某些情況下,這可能變得效率低下。

另一種實現方式(我使用的一種方式)更適合於讀寫比高的情況。

如果讀取是非常常見的情況,那麼你可以在一個更加永久的數據庫後端實現你的標籤功能(我假設你有一個分類數據庫,所以它只需要一些額外的表格在這裏)。

在緩存中寫入項目時,將密鑰和標籤存儲在一個簡單的表格中(密鑰和標籤列,每個標籤在密鑰上一行)。編寫按鍵很簡單:「從cache_tags刪除其中id =:關鍵;的foreach(標籤爲標籤)插入cache_tags值(:關鍵,是:標籤); (NB用在真正實現了一套擴展插入語句)

。當使標籤無效時,只需遍歷所有具有該標籤的鍵:(從cache_tags中選擇tag,其中tag =:tag;)並使每個鍵無效(並且可選地從cache_tags表中刪除鍵以便整理)

如果某個密鑰從memcache中被逐出,那麼cache_tags元數據將會過時,但這通常是無害的。當您試圖使擁有該標籤的密鑰失效時,最多會導致效率低下,導致效率低下已被驅逐

這種方法提供了「免費」加載(不需要檢查標籤),但是花費很大(本來已經很昂貴,否則它不需要首先被緩存)。因此,根據您的使用情況以及預期的負載模式和使用情況,我希望您的原始策略(對負載進行更嚴格的檢查)或「數據庫支持的標記」策略將滿足您的需求。

HTHS

+0

何是啊,我也沒多想這個問題!是的,我已經想過可靠的標籤緩存後端,我想我會使用它 – Kakawait

+0

什麼是您可靠的後端? RDBM像Redis一樣是MySQL還是NoSQL?如果MySQL是什麼你的存儲引擎? – Kakawait

+0

我做的事情非常相似。我有一個非常簡單的mysql cache_keys表。它有兩列,'keyname'和'expires'。任何時候我將某些內容保存到memcached中,我還會對cache_keys表進行插入/更新以記錄密鑰。我不記錄所有緩存鍵,只是我知道我需要能夠手動清除。然後,我使用一個php腳本對數據庫執行通配符查詢,以查找具有給定前綴的鍵,並將它們從memcached和db中刪除。我也可以通過這種方式清除過期的密鑰。它可以工作,但清除緩存會破壞MySQL。當我們有300個同時在線的用戶時,工作得很好,800+以上的用戶不太好。 – minorgod