缓存失效算法

时间:2017-07-15 13:47:43

标签: caching invalidation

我正在考虑在Web服务器中缓存动态内容。我的目标是通过返回缓存的HTTP响应来桥接整个处理,而不会打扰DB(或Hibernate)。这个问题不是在现有的缓存解决方案之间进行选择;我目前关注的是失效。

我确定,基于时间的失效根本没有意义:每当用户改变任何东西时,他们都希望立即看到效果,而不是几秒甚至几分钟。缓存几分之一秒是没用的,因为在如此短的时间内没有重复请求相同的数据(因为大多数数据都是用户特定的)。

对于每次数据更改,我都会收到一个事件,可以根据更改的数据使用它来使一切无效。当请求同时发生时,有两个与时间相关的问题:

  • 无效可能来得太晚,甚至可能会向更改它们的客户提供陈旧数据。
  • 失效完成后,长时间运行的请求可能会完成,其陈旧数据可能会被放入缓存中。

这两个问题彼此相反。我想,通过使用每个客户端ReadWriteLock部分序列化来自同一客户端的请求,可以轻松解决前者问题。所以,让我们忘记吧。

后者更严重,因为它基本上意味着失去失效并永远(或太长)提供陈旧数据。

我可以想象一个解决方案,比如在变更发生之前每个请求开始后重复失效,但这听起来相当复杂和耗时。我想知道是否有任何现有的缓存支持这一点,但我主要对如何完成这一操作感兴趣。

澄清

问题是一个简单的竞争条件:

  • 请求A执行查询并获取结果
  • 请求B进行了一些更改
  • B发生的失效
  • 请求A(因任何原因被推迟)完成
  • 请求A的过时响应被写入缓存

2 个答案:

答案 0 :(得分:2)

要解决争用条件,请添加时间戳(或计数器)并在设置新缓存条目时检查此时间戳。 这可确保不会缓存过时的响应。

这是一个伪代码:

//set new cache entry if resourceId is not cached
//or if existing entry is stale
function setCache(resourceId, requestTimestamp, responseData) {
    if (cache[resourceId]) {
        if (cache[resourceId].timestamp > requestTimestamp) {
            //existing entry is newer
            return;
        } else
        if (cache[resourceId].timestamp = requestTimestamp) {
            //ensure invalidation
            responseData = null;
        }
    }

    cache[resourceId] = {
        timestamp: requestTimestamp,
        response: responseData
    };
}

假设我们收到2个相同资源“foo”的请求:

  • 请求A(在00:00:00.000收到)执行查询并获取结果
  • 请求B(在00:00:00.001收到)做了一些更改
  • 通过调用setCache("foo", "00:00:00.001", null)
  • 发生B因无效
  • 请求A完成
  • 请求A调用setCache("foo", "00:00:00.000", ...)将过时的响应写入缓存但由于现有条目较新而失败

这只是基本机制,所以还有改进的余地。

答案 1 :(得分:1)

我认为你没有意识到(或者不想明确地呼唤)你在询问缓存同步策略之间的选择。有几个众所周知的策略:"缓存旁边","通读","通过"和#34;写后面"。例如在这里阅读:A beginner’s guide to Cache synchronization strategies。它们提供各种级别的缓存一致性(在您调用时失效)。

您的选择应取决于您的需求和要求。

听起来你已经选择了"写在后面"策略(队列或延迟缓存失效)。但是从您的担忧中听起来您已经错误地选择了它,因为您担心缓存读取不一致。

所以,你应该考虑使用"缓存而不是#34;或"读/写通过"策略,因为那些提供更好的缓存一致性。它们都是同一个东西的不同风格 - 始终保持缓存一致。如果你不关心缓存的一致性,那么好吧,继续"写下",然后这个问题变得无关紧要。

体系结构广泛,我永远不会提高事件来使缓存失效,因为您似乎已将其作为业务逻辑的一部分,而它只是一个基础架构问题。作为读/写操作的一部分而不是高速缓存(或队列失效),而不是其他地方。这使缓存成为您基础架构的一个方面,而不是其他任何方面。