“计算机科学中只有两个难题:缓存失效和命名。”
菲尔·卡尔顿
是否有使缓存无效的一般解决方案或方法;知道什么时候条目是陈旧的,所以可以保证您总是能获得最新数据?
例如,考虑一个getData()从文件获取数据的函数。它根据文件的上次修改时间对其进行缓存,并在每次调用时对其进行检查。 然后,添加第二个函数transformData()来转换数据,并缓存其结果,以备下次调用该函数时使用。它不知道文件- 如何添加依赖项,如果文件被更改,此缓存将变为无效?
getData()
transformData()
您可以在getData()每次调用时transformData()进行调用,并将其与用于构建缓存的值进行比较,但这最终可能会非常昂贵。
您正在谈论的是生命周期依赖链,一件事依赖于另一件事,可以在其控制范围之外进行修改。
如果你有一个幂函数,从a,b到c那里,如果a和b相同,则c是相同的,但检查的成本b是高的,那么你可以:
a
b
c
你不能吃蛋糕也不能吃…
如果您可以a在顶部放置一个额外的缓存,那么这将对最初的问题产生一点影响。如果您选择1,那么您将拥有任意的自由度,因此可以缓存更多,但必须记住要考虑缓存值的有效性b。如果选择2,则您仍必须b每次都检查一次,但可以退回缓存以检查a是否b签出。
如果您分层缓存,则必须考虑是否由于综合行为而违反了系统的“规则”。
如果您知道a始终有效b,那么您可以这样安排您的缓存(伪代码):
private map<b,map<a,c>> cache // private func realFunction // (a,b) -> c get(a, b) { c result; map<a,c> endCache; if (cache[b] expired or not present) { remove all b -> * entries in cache; endCache = new map<a,c>(); 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; }
显然x,只要在每个阶段新添加的输入的有效性与:和:的a:b关系匹配,连续的分层(say )都是微不足道的。x``b``x``a
x
x``b``x``a
但是,您很有可能获得三个输入,其有效性完全独立(或循环),因此不可能分层。这意味着标记为//重要的行必须更改为
如果(endCache [a] 过期或 不存在)