Cache Invalidation — Is there a General Solution?
"There are only two hard problems in Computer Science: cache invalidation and naming things."
菲尔KarltonP></
is there or method to a通用解决方案invalidating缓存;当an entry is基本知道,你是我永远的移调to get保鲜日期?P></
考虑到
你可以呼叫
你所说的是生命周期依赖链,一件事依赖于另一件事,而另一件事可以在它的控制之外被修改。
如果你有一个从
你不能吃蛋糕…
如果您可以在顶部覆盖基于
如果对缓存进行分层,则必须考虑是否由于组合行为而违反了系统的"规则"。
如果您知道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private map<b,map> cache // private func realFunction // (a,b) -> c get(a, b) { c result; map endCache; if (cache[b] expired or not present) { remove all b -> * entries in cache; endCache = new map(); add to cache b -> endCache; } else { endCache = cache[b]; } if (endCache[a] not present) // important line { result = realFunction(a,b); endCache[a] = result; } else { result = endCache[a]; } return result; } |
显然,只要在每个阶段,新增加的输入的有效性与
但是,您很可能会得到三个有效性完全独立(或是循环的)的输入,因此不可能进行分层。这意味着标有//重要的行必须改为
if (endCache[a] expired or not present)
缓存失效中的问题是,在我们不知道的情况下,内容会发生变化。因此,在某些情况下,如果有其他事情知道并能通知我们,解决方案是可能的。在给定的示例中,getdata函数可以钩住文件系统,该系统知道对文件的所有更改,而不管哪个进程更改了文件,而这个组件反过来可以通知转换数据的组件。
我不认为有任何一般的魔术解决办法可以让问题消失。但在许多实际情况下,很可能会有机会将基于"轮询"的方法转换为基于"中断"的方法,这会使问题简单地消失。
功能反应式编程(FRP)在某种意义上是解决缓存失效的一种通用方法。
原因如下:在玻璃钢术语中陈旧的数据被称为故障。玻璃钢的目标之一是保证无故障。
在这篇"玻璃钢的本质"的演讲中,以及在这个问题的答案中,对玻璃钢作了更详细的解释。
在谈话中,
FRP隐藏了与依赖关系图关联的管道代码,并确保没有过时的
我能想到的另一种方法(不同于FRP)是将计算值(类型为
将这个想法与在这种编写器monad上运行的组合器结合起来,如果您只使用这些组合器来计算一个新的
因此,每次更改计算
所以,当你用一个给定的
当然,只有当你阅读的次数比写作的次数多的时候,这才有回报。
第三种实用的方法是在数据库中使用物化视图,并将它们用作缓存。他们还致力于解决失效问题。当然,这限制了将可变数据连接到派生数据的操作。
如果每次进行转换时都要使用getData(),那么就消除了缓存的全部好处。
对于您的示例,似乎有一个解决方案,当您生成转换的数据时,还可以存储数据所生成的文件的文件名和上次修改时间(您已经将其存储在getdata()返回的任何数据结构中,因此您只需将该记录复制到transformdata()返回的数据结构中),然后d然后当您再次调用transformdata()时,检查文件的最后修改时间。
我现在正在研究一种基于Postshap和记忆功能的方法。我已经从我的导师那里运行过了,他同意这是一个以内容无关的方式实现缓存的好方法。
每个函数都可以用指定其有效期的属性进行标记。以这种方式标记的每个函数都是内存化的,结果存储在缓存中,函数调用的散列和参数用作键。我在后台使用Velocity,它处理缓存数据的分发。
没有一般的解决方案,但:
缓存可以充当代理(拉)。假设您的缓存知道最后一个源更改的时间戳,当有人调用
getData() 时,缓存会向源请求它的最后一个更改的时间戳,如果相同,它会返回缓存,否则它会用源更改更新其内容并返回其内容。(一个变化是客户端直接发送请求的时间戳,如果其时间戳不同,则源只返回内容。)您仍然可以使用通知进程(push),缓存观察源,如果源发生更改,它将向缓存发送一个通知,然后将其标记为"脏"。如果有人调用
getData() ,缓存将首先更新到源,删除"脏"标志,然后返回其内容。
一般来说,选择取决于:
- 频率:许多对
getData() 的调用更喜欢推送,以避免getTimeStamp函数淹没源。 - 您对源的访问:您拥有源模型吗?否则,很可能无法添加任何通知进程。
注意:由于使用时间戳是HTTP代理工作的传统方式,另一种方法是共享存储的内容散列。我知道两个实体在一起更新的唯一方法就是要么我叫你(拉),要么你叫我…(推),就这些。
Is there a general solution or method to creating a cache, to know when an entry is stale, so you are guaranteed to always get fresh data?
不,因为所有数据都不同。有些数据可能在一分钟后"过时",有些在一小时后"过时",而有些数据可能会在几天或几个月内保持良好状态。
对于您的具体示例,最简单的解决方案是为文件提供"缓存检查"功能,您可以从
缓存很难,因为您需要考虑:1)缓存是多个节点,需要协商一致2)失效时间3)多发性起跳/起跳时的比赛状态
这是一个很好的读物:https://www.confluent.io/blog/turning-the-database-inside-out-with-apache-samza/
也许缓存遗忘算法将是最通用的(或者至少,更少依赖于硬件配置),因为它们将首先使用最快的缓存,然后从那里继续。这是麻省理工学院的一个讲座:缓存遗忘算法