管理缓存失效

19

我想知道你们如何管理缓存失效。考虑到缓存中可能会有数百或数千个对象,这些对象可能会被不同的算法或规则触发。你们是如何跟踪这些对象的呢?

是否可以从数据库表中引用关系,并以某种方式强制执行?

请原谅我之前没有做过任何缓存相关的工作。


你说的“keep track of it”是什么意思? - Pascal Thivent
我不明白某件事。你为什么需要追踪这个?顺便问一下,你是在实现自己的缓存吗? - Pascal Thivent
是的,我正在实现自己的缓存。我需要跟踪关系,以便我们始终知道哪些类型的失效方法正在影响哪些对象。 - super9
2
如果你以前没有做过缓存,那么获取一个现有的实现并学习它。这是一个复杂的问题,没有一个解决方案。 - skaffman
缓存失效是一个非常棘手的问题,需要正确处理。它在很大程度上取决于您项目的具体情况。 - Eli
显示剩余2条评论
7个回答

13
你的缓存层的目的应该基本上是反映数据库中对应数据,但要比数据库更快地提供它,或者至少在不使数据库繁忙的情况下提供它。为了实现这一点,你有两个解决方案:
1.知道缓存中每个存储项的确切生命周期。 2.保持缓存与数据库同步更新。
第一个方案非常罕见,但处理起来很容易:定期更新缓存。
第二个方案是你在项目中最有可能遇到的,只需在成功将对象添加到数据库后立即将其添加到缓存中;在成功更新对象时,立即在缓存中更新对象;在成功删除对象时,立即从缓存中删除对象。
如果你的代码足够清晰明了,那么在其上实现有效的缓存策略应该很容易。我之前发布的答案中还有更多关于缓存以及如何做得更好的内容。希望这些对你有所帮助 :)

1
但是如果代码不够“干净”怎么办呢?(假设这不是你的代码,而是遗留代码)另外,如果系统中没有数据库,而是与慢速设备通信,该怎么办呢? - avp
"足够简洁"的代码会让事情变得更容易,但我的观点与糟糕的遗留代码相同。重要的是你必须坚持的逻辑:添加代码以确保缓存数据始终与数据库中的数据保持最新。混乱的代码只会让你花费更多时间来确保每个数据库操作都后跟着正确的缓存添加/更新/删除操作。(接下来的评论中有更多内容...) - Nicolas
与其使用慢速设备而不是数据库或其他你正在使用的工具,这并不是一个“问题”。重要的是通常使用最新数据,而不是每次都命中缓存。如果您更注重低延迟而不是数据新鲜度,我认为在优化之前您需要处理其他问题。 - Nicolas

11

正如你所发现的那样,这不像简单地更新新闻故事缓存那么简单。还有其他关系,例如需要更新的最新新闻列表。

最简单的方法是将所有相关对象联系起来。我以前使用过“缓存组”的概念。继续以新闻为例,在“缓存组”'news'中将包括:新闻故事、各种新闻故事列表及包含新闻故事的任何其他内容。

当我编辑新闻故事时,系统会识别出它需要更新'news'缓存组,并执行以下过程...

  1. 在保存更新之前获取每个对象
  2. 保存
  3. 再次获取对象,如果不同则更新各种缓存

当然,这只是一个非常简单的例子。更好的做法是编写代码始终将对象保持在缓存中的状态。

如果您添加了新闻文章的标签,您的代码可以只将这些更改写入数据库,但如果您更新新闻文章对象和相关标签对象,则这两个对象都可以“知道”它们已更改(只需设置hasChanged = true),然后您就可以自动更新缓存并保存到数据库中。


2
+1 对于缓存组非常有用。我在过去的几个小时里在互联网上寻找如何处理缓存失效的想法,这是我第一次看到有人提到它。 - Aaron

4

如果你正在使用SQL Server 2005或更高版本以及.NET,你可能需要考虑使用SQLDependency类。这个类使用SQL Server Service Broker来通知你数据发生了某些修改。你可以将其用作触发器来使缓存失效。需要注意的是,这仅适用于使用这些技术的情况。


给那位给这个答案点了踩的人,如果你能解释一下原因,对社区会有好处。 - Jacob

3
如果您的实体之间关联紧密,并且需要在任何相关实体的更改时更新缓存,最好的方法是将其结构化为树形结构。
如果obj B是obj A的外键,且A的某些属性发生更改,则也需要更新B的缓存。
使用基于树形结构的方式,如果识别到“分支”中的更改,则更新所有“叶子”的缓存。或者如果“根”被更新,则更新所有“分支”+“叶子”的缓存直至最底层。考虑层次结构。

2

0

对于一般解决方案,你可以看一下Juha提供的链接。

但根据你的问题,我想描述一下我们项目中是如何处理的。
我们没有使用任何通用的缓存解决方案。我们的缓存是逐渐增长起来的。最初我们并没有打算使用缓存。但后来缓存就出现了。由于缓存是最近添加到系统中的,它并不知道任何“数据库”或其他“智能东西”。相反,我们会仔细检查是否有人更改了缓存。因此,我将我们的缓存称为“算法驱动的”。

(唯一真正必要的通用功能是处理缓存失效的功能。
另一个值得注意的问题是与客户端的身份识别:如果你有多个客户端,一个缓存可能不够...但对于这两个问题,只是添加了特定的解决方案,并非通用解决方案!)

我知道,描述这样一个基本的功能可能听起来很傻。有人可能会说“我们一开始就应该使用正常的缓存”。但你知道,在现实生活中,有时候有些事情是你无法控制的,你只能尽力而为。

所以总结一下:我们不需要通用解决方案。我们的算法控制着缓存。这使得缓存保持较小的体积(在代码和运行时内存中)。这就是我们的方法。


很好,希望能得到更多像这样的答案。 - super9

0
只是想知道你们如何管理缓存失效。考虑到缓存中可能有不同算法或规则触发的对象(数百或数千个),你们如何跟踪所有这些呢?
我不确定是否理解清楚了这一部分,但我认为你应该定义不同的"区域"(就像Hibernate术语中的那样),每个区域都有自己的内容和规则。
有没有办法从数据库表中引用关系并以某种方式强制执行呢?

在我看来,持久层是最好的地方来处理这个问题,因为它知道持久和可能被缓存的实体发生了什么。例如,Hibernate支持(二级)缓存,并允许定义二级缓存区域的名称,每个实体的缓存策略(只读、读写、非严格读写、事务性)。Hibernate实际上定义了一个接口,并允许根据您的需求插入缓存实现(缓存类型、支持的策略、集群支持)。

请原谅我,因为我以前从未做过任何缓存。

根据您的需求复杂程度,这可能不是一项简单的任务。也许您应该使用或查看现有的解决方案。在Java世界中,EHCache, OSCache, SwarmCache, JBoss Cache 2 都是失效缓存(或支持它)。这只是一个建议,因为您没有提到任何语言。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接