7

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

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

然而,问题来了:

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

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

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

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

谢谢。

4

8 回答 8

7

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

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

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

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

谢谢!

于 2008-09-22T07:46:45.050 回答
5

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

ExtendedMemcache->set方法接受 3 个 args ( $strGroup, $strKey, ) 当您调用 set 时,它会将, 和,$strValue之间的关系存储在受保护的属性中,然后继续将to关系存储在.$strGroup$strKey$strKey$strValuememcache

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

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

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

于 2008-09-20T23:19:13.860 回答
5

您可能还想查看存储缓存数据的成本,根据您的工作量和 CPU 成本,以及缓存将如何购买您。

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

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

于 2008-09-21T03:31:12.083 回答
2

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

于 2008-09-20T21:48:46.963 回答
1

一种可能的解决方案是不对论坛中的线程缓存进行分页,而是将线程信息放入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]);
}

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

于 2008-09-20T21:49:05.213 回答
1

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

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

于 2008-09-20T23:47:57.643 回答
1

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

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

于 2008-09-21T11:07:35.823 回答
1

对飞龙草的回应:

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

例如

get seqno_mygroup
23

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

然后简单地“删除”该组:

incr seqno_mygroup

瞧:

get seqno_mygroup
24

get mygroup24_mykey
...empty

ETC..

于 2009-01-13T05:41:52.720 回答