【问题标题】:Caching paginated results, purging on update - how to solve?缓存分页结果,清除更新 - 如何解决?
【发布时间】:2010-09-11 16:32:36
【问题描述】:

我创建了一个论坛,我们正在实施一个 apc 和 memcache 缓存解决方案来节省数据库的一些工作。

我开始使用诸如“Categories::getAll”之类的键来实现缓存层,如果我有用户特定的数据,我会在键中附加用户 ID 之类的东西,这样你就会得到"User::getFavoriteThreads|1471"。当用户添加新的收藏线程时,我会删除缓存键,然后它会重新创建条目。

但是,问题来了:

我想缓存论坛中的帖子。很简单,“Forum::getThreads|$iForumId”。但是...使用分页,我必须将其拆分为几个缓存条目,例如

"Forum::getThreads|$iForumId|$iLimit|$iOffset".

没关系,直到有人在论坛中发布新帖子。我现在必须删除"Forum::getThreads|$iForumId" 下的所有键,无论限制和偏移量是多少。

解决这个问题的好方法是什么?我真的不想遍历所有可能的限制和偏移,直到找到不再匹配的东西。

谢谢。

【问题讨论】:

    标签: php caching pagination memcached


    【解决方案1】:

    只是一个更新: 我认为 Josh 关于数据使用的观点非常好。 人们不太可能继续查看论坛的第 50 页。

    基于这个模型,我决定在每个论坛缓存 90 个最新的帖子。在获取函数中,我检查限制和偏移量以查看指定的线程片是否在缓存内。如果它在缓存限制内,我使用 array_slice() 检索正​​确的部分并返回它。

    这样,我可以为每个论坛使用一个缓存键,并且清除/更新缓存只需很少的努力:-)

    我还想指出,在其他资源较多的查询中,我使用了 flungabunga 的模型,存储键之间的关系。不幸的是,堆栈溢出不会让我接受两个答案。

    谢谢!

    【讨论】:

      【解决方案2】:

      您可能还想了解一下存储缓存数据的成本(根据您的工作量和 CPU 成本),以及缓存会为您带来的收益。

      如果您发现 80% 的论坛浏览量都在查看主题的第一页,那么您可以决定只缓存该页面。这意味着缓存读取和写入都更容易实现。

      用户最喜欢的线程列表也是如此。如果这是每个人都很少访问的东西,那么缓存可能不会太多地提高性能。

      【讨论】:

        【解决方案3】:

        我已经设法通过使用自定义类(例如 ExtendedMemcache)扩展 memcache 类来解决这个问题,该类具有受保护的属性,其中包含组到键值的哈希表。

        ExtendedMemcache->set 方法接受 3 个参数($strGroup,$strKey,$strValue) 当你调用 set 时,它会将$strGroup$strKey 之间的关系存储在受保护的属性中,然后继续将$strKey$strValue 的关系存储在memcache 中。

        然后您可以向ExtendedMemcache 类添加一个名为“deleteGroup”的新方法,当传递一个字符串时,该方法将找到与该组关联的键,并依次清除每个键。

        应该是这样的: http://pastebin.com/f566e913b 我希望所有这些都有意义并且对你有用。

        PS。我想如果您想使用静态调用,可以将受保护的属性保存在 memcache 本身的自己的密钥下。只是一个想法。

        【讨论】:

          【解决方案4】:

          您实际上是在尝试缓存视图,这总是很棘手。相反,您应该尝试仅缓存数据,因为数据很少更改。不要缓存论坛,缓存线程行。然后你的 db 调用应该只返回一个 id 列表,你已经在你的缓存中。 db 调用将在任何 MyISAM 表上快速减轻,然后您不必进行大连接,这会占用 db 内存。

          【讨论】:

          • 我不知道您在考虑哪种表结构,但是如果您有一个线程表,则无论如何都不需要连接。使用缓存的好处可以忽略不计。
          • 这可能是一个很好的解决方案,虽然它需要我进行相当大的重写 - 有很多数据要检索(线程中的帖子数量,作者 nick 必须从用户那里加入表,视图数量等)。感谢您的建议!
          • 听起来你可以通过非规范化来实现等效的加速。将帖子数、作者姓名、查看次数等存储在线程记录中。
          【解决方案5】:

          一种可能的解决方案是不对论坛中的线程缓存进行分页,而是将线程信息放入Forum::getThreads|$iForumId。然后在你的 PHP 代码中只为给定页面拉出你想要的那些,例如

          $page = 2;
          $threads_per_page = 25;
          $start_thread = $page * $threads_per_page;
          
          // Pull threads from cache (assuming $cache class for memcache interface..)
          $threads = $cache->get("Forum::getThreads|$iForumId");
          
          // Only take the ones we need
          for($i=$start_thread; $i<=$start_thread+$threads_per_page; $i++)
          {
              // Thread display logic here...
              showThread($threads[$i]);
          }
          

          这意味着您确实需要做更多的工作来在每个页面上将它们拉出,但现在只需要担心在更新/添加新线程时会使缓存失效。

          【讨论】:

          • 我想过这个问题,但是我正在将一个现有的论坛转换为这个,一个论坛有 220 000 个线程,这样存储的数据会很多。如果数据较少,这可能是最好的解决方案。谢谢!
          【解决方案6】:

          fungabunga: 您的解决方案与我正在寻找的非常接近。唯一阻止我这样做的是必须在每次请求后将关系存储在 memcache 中并将它们加载回来。

          我不确定这会对性能造成多大影响,但这似乎有点低效。我会做一些测试,看看结果如何。感谢您提供结构化的建议(以及一些代码,谢谢!)。

          【讨论】:

            【解决方案7】:

            在没有确凿的事实可以衡量的情况下进行这种优化时要非常小心。

            大多数数据库都有多个级别的缓存。如果这些调整正确,数据库在缓存方面可能会比您自己做的更好。

            【讨论】:

              【解决方案8】:

              响应 flungabunga:

              实现分组的另一种方法是将组名加上序列号放入键本身,并增加序列号以“清除”组。您将每个组的当前有效序列号存储在其自己的密钥中。

              例如

              get seqno_mygroup
              23
              
              get mygroup23_mykey
              <mykeydata...>
              get mygroup23_mykey2
              <mykey2data...>
              

              然后简单地“删除”组:

              incr seqno_mygroup
              

              瞧:

              get seqno_mygroup
              24
              
              get mygroup24_mykey
              ...empty
              

              等等。

              【讨论】:

                猜你喜欢
                • 2013-11-13
                • 2012-11-12
                • 2019-09-01
                • 2021-07-14
                • 1970-01-01
                • 1970-01-01
                • 2015-08-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多