缓存失效策略

35
在我的当前应用程序中,我们处理一些很少更改的信息
为了提高性能,我们希望将它们存储在缓存中
但是问题在于每当更新这些对象时都要使其失效
我们还没有确定缓存产品。
因为我们正在Azure上构建此应用程序,所以可能会使用Azure Redis缓存
一种策略是在更新API中添加代码来使缓存中的对象无效。
我不确定这是否是一种干净的方法?
我们不想基于时间(TTL)进行缓存过期
请您建议一些其他用于缓存失效的策略吗?
2个回答

43

在更新阶段使缓存失效是一种可行的方法,在过去被广泛使用。

当更新发生时,您有两个选择:

  1. 您可以尝试在更新操作期间设置新值,或者
  2. 只需删除旧值并在读取操作期间进行更新。

如果您想要一个LRU cache,那么UPDATE可能只会删除旧值,并且第一次获取对象时,您将在从实际数据库读取后再次创建它。但是,如果您知道您的缓存非常小,并且您正在使用另一个主数据库来处理与数据大小不同的问题,则可以直接在UPDATE期间进行更新。

然而,所有这些都不足以完全保持一致性。
当您写入DB时,例如,Redis缓存可能无法使用几秒钟,因此数据仍然在两者之间不同步。
在这种情况下,您该怎么办?
您可以同时使用几个选项。

  1. 无论如何都要设置TTL,以便最终刷新损坏的数据。
  2. 使用懒惰的读取修复。当您从数据库中读取时,请不时检查主键是否匹配。如果不匹配,则更新缓存项(或删除它)。
  3. 使用纪元或类似方式访问数据。并非总是可能,但有时您会访问关于给定对象的缓存数据。如果可能,每次修改对象时,您可以更改对象ID/句柄,以使在缓存中无法访问旧数据:每个键名称都引用您对象的特定版本。

因此,删除更新缓存和读取缓存的基本策略,但您可以使用其他附加系统最终修复不一致性。

实际上,除了使用上述选项之外,还有另一种选择,即使用Redis SCAN 的后台进程逐个验证键是否存在不一致性。此过程可能很慢,并且可以针对数据库的副本运行。

正如您在这里看到的,主要思想始终相同:如果缓存更新失败,请不要使其成为永久问题,可能会一直存在,给它一个在以后修复自己的机会。


1
当同时调用获取操作并且缓存中没有该值时,del-cache-on-update 可能会导致一次大规模的请求。这些请求将全部发送到数据库。可以使用锁定或其他策略来避免这种情况。因此,在读取期间更新缓存可能是更好的选择。 - Konstantin Milyutin
del-cache-on-update如何防止由于并发读写而导致的数据不一致?如果一个请求从数据库中读取了旧值,然后另一个请求将新值更新到数据库中,使缓存失效,但是读请求使用旧数据更新了缓存,我们就会遇到问题。 - bornfree

1
我认为Lambda架构适用于您的用例。
  1. 实时更新以进行即时业务使用
  2. 批量数据加载以修复任何失败的记录
  3. 批量数据加载以删除任何无效/归档记录。
对于实时更新,您需要在应用程序的代码库上工作,将数据写入DB和缓存。
对于批量数据加载,您可以查看数据摄取工具,例如Logstash / fluentd,以从数据库中“拉”最新数据并将其推送到缓存。可以基于始终增加的列(ID号码或时间戳)完成此操作。
我在我的端口使用Oracle数据库。 Logstash JDBC插件在提取最新记录方面表现不错。 logstash输出可以格式化并打印到Redis可以使用的文件中。我编写了一个小型bash脚本来编排这个过程。测试了300万条记录,效果还不错。

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