【问题标题】:Memcache tags simulationMemcache 标签模拟
【发布时间】:2012-06-09 06:22:26
【问题描述】:

Memcached 是一个很棒的可扩展缓存层,但它有一个大问题(对我来说)它无法管理标签。并且标签对于组失效非常有用。

我做了一些研究,我知道一些解决方案:

我最喜欢的解决方案之一是命名空间,此解决方案在 memcached wiki 上进行了说明。

但是我不明白为什么我们要在键缓存上集成命名空间?

据我了解关于命名空间的技巧是:要生成密钥,我们必须获取命名空间的值(在缓存上)。如果namespace->value 缓存条目被驱逐,我们就无法再计算获取缓存的好键...所以这个命名空间的缓存虚拟无效(我说虚拟是因为缓存仍然存在但我们无法再计算访问密钥)。

那么为什么我们不能简单地实现类似的东西:

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] 被驱逐,我们就不能再使 tag1 键失效。但随着

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;
   }
}

这是伪代码

如果所有这些标签都可用,我们肯定会返回缓存。

首先我们可以说它是“每次你需要获取缓存时,我们必须检查(/获取)X标签,然后检查数组”。但是对于命名空间,我们还必须检查(/get)命名空间来检索命名空间值,主要的区别是在一个数组下迭代...... 但我不认为键会有很多标签(我无法想象我的应用程序有超过 10 个标签/键),所以在大小为 10 的数组下迭代它的速度非常快..

所以我的问题是:有人已经考虑过这个实现了吗?限制是什么?我是不是忘记了什么?等等

或者我可能误解了命名空间的概念......

PS:我不是在寻找另一个缓存层,比如 memcached-tag 或 redis

【问题讨论】:

    标签: caching memcached


    【解决方案1】:

    我认为你在这个实现中忘记了一些东西,但修复起来很简单。

    考虑多个key共享一些标签的问题:

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

    假设您加载了 key1。您仔细检查 tag1 和 tag2 是否存在。这很好,并且可以加载密钥。

    然后 tag1 以某种方式从缓存中逐出。

    然后您的代码会使 tag1 无效。这应该删除 key1 和 key2 但因为 tag1 已被驱逐,这不会发生。

    然后您添加一个新项目 key3。也指tag1:

    key3 -> tag1
    

    保存此密钥时,会(重新)创建 tag1:

    tag1 -> key3
    

    稍后,当再次从缓存中加载 key1 时,您检查伪代码以确保 tag1 存在成功。并且允许加载来自 key1 的(陈旧)数据。

    显然,解决此问题的一种方法是检查 tag1 数据的值,以确保您正在加载的键列在该数组中,并且仅在此情况下才认为您的键有效。

    当然,根据您的用例,这可能会出现性能问题。如果给定的键有 10 个标签,但每个标签都被 10k 个键使用,那么您必须在 10k 个项目的数组中搜索才能找到您的键,并在每次加载时重复 10 次。

    在某些时候,这可能会变得低效。

    当您的读写比率非常高时,另一种实现(也是我使用的)更合适。

    如果读取是非常常见的情况,那么您可以在更永久的数据库后端实现您的标签功能(我假设您有一个数据库,所以它只需要几个额外的表)。

    当您在缓存中写入项目时,您将键和标签存储在一个简单的表中(键和标签列,键上的每个标签对应一行)。写一个key很简单:“delete from cache_tags where id=:key; foreach (tags as tag) insert into cache_tags values(:key, :tag); (注意在实际 impl 中使用扩展插入语法)。

    当使标签失效时,只需遍历具有该标签的所有键:(从 cache_tags 中选择键,其中 tag=:tag;)并使它们中的每一个都失效(并且还可以选择从 cache_tags 表中删除键以进行整理) .

    如果从 memcache 中逐出密钥,则 cache_tags 元数据将过期,但这通常是无害的。当您尝试使具有该标签但已被驱逐的密钥无效时,它最多会导致效率低下。

    这种方法提供了“免费”加载(无需检查标签)但节省了昂贵的费用(无论如何这已经很昂贵,否则它不需要首先被缓存!)。

    因此,根据您的用例以及预期的负载模式和使用情况,我希望您的原始策略(对负载进行更严格的检查)或“数据库支持的标签”策略能够满足您的需求。

    HTH

    【讨论】:

    • 呵呵,我没想到这个问题!是的,我已经考虑过标签缓存的可靠后端,我想我会使用它
    • 你可靠的后端是什么? RDBM 像 MySQL 还是 NoSQL 像 Redis?如果是 MySQL,你的存储引擎是什么?
    • 我做的事情非常相似。我有一个非常简单的 mysql cache_keys 表。它有两列,“keyname”和“expires”。每当我将某些内容保存到 memcached 时,我也会对 cache_keys 表进行插入/更新以记录密钥。我没有记录所有缓存键,只记录我知道我需要能够手动清除的那些。然后我使用 php 脚本对数据库进行通配符查询,以查找具有给定前缀的键并将它们从 memcached 和数据库中删除。我也可以通过这种方式清除过期的密钥。它有效,但清除缓存会破坏 MySQL。当我们同时拥有 300 个用户时工作正常,但在 800 多个用户时效果不佳。
    • 我觉得你可以试试更好的一对:Redis + Memcached。 Redis 是一个内存中的 nosql 数据库,因此它可以支持比 mysql 更多的并发请求。我使用这种组合,整个系统在同时拥有 1000 多个用户的网站上运行
    猜你喜欢
    • 2018-03-09
    • 2011-10-18
    • 2013-06-19
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    • 2012-04-07
    • 2020-11-02
    相关资源
    最近更新 更多