2012-06-05 39 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