许多人在处理缓存更新时,采用了一种常见的错误逻辑:先删除缓存,再更新数据库。这种逻辑在某些并发场景下会导致缓存中的数据变得“脏”,即缓存中的数据与数据库中的数据不一致。想象一下,当两个并发操作同时进行,一个是更新操作,另一个是查询操作时,如果更新操作删除了缓存,而查询操作未能命中缓存并从老数据中读取,那么缓存中的数据就会保持老旧状态,无法得到更新。
尽管这种逻辑被广泛使用,但让我们探讨一下更为稳健的缓存更新设计模式。这里,我们暂不考虑更新缓存和更新数据库可能的事务失败情况,假设两者都能成功完成。
其中一种常见的模式是Cache Aside Pattern,也被称为缓存旁路模式。其核心逻辑如下:
失效:应用程序首先尝试从缓存中获取数据。如果未成功,则转向数据库获取数据,并将新数据成功放入缓存。
命中:当应用程序从缓存中获取数据时,直接返回数据。
更新:在更新数据时,首先将数据存入数据库,然后使缓存失效。
在并发场景下,当一个查询操作和一个更新操作同时进行时,由于缓存依然有效,查询操作将获取未更新的数据。但随后,更新操作使缓存失效,后续的查询操作将从数据库中获取最新数据。这样就避免了缓存数据过时的问题。
Facebook在其论文《Scaling Memcache at Facebook》中也采用了类似的策略。为什么Facebook选择删除缓存而不是直接更新?主要是为了避免两个并发写操作导致的脏数据问题。使用Cache Aside Pattern仍然可能面临并发问题。例如,在读操作未命中缓存并转向数据库时,如果有一个写操作完成了数据库的更新并使缓存失效,那么之前的读操作可能会将老数据放入缓存。虽然这种情况理论上可能出现,但实际上发生的概率相对较低。
另一种模式是Read/Write Through Pattern,这种模式将数据库更新的操作委托给缓存层处理。在这种模式下,应用层只需与单一的存储后端交互,而存储后端会自己维护其缓存。这种模式的优点在于简化了应用层的逻辑,因为缓存层负责处理与数据库的交互。它也有自己的挑战和复杂性需要考虑。
2.1 读透(Read Through)策略
读透策略是在查询操作时更新缓存的一种机制。当缓存失效(无论是过期还是由于LRU策略被替换)时,不同于Cache Aside由调用方负责加载数据到缓存,读透策略是由缓存服务自己来加载数据,这一过程对应用方是透明的。
2.2 写透(Write Through)策略
写透策略与读透策略相似,但在数据更新时发挥作用。如果没有命中缓存,它直接更新数据库,然后返回;如果命中了缓存,则先更新缓存,然后由Cache自己同步更新数据库。如下图所示,我们可以把Memory理解为例子中的数据库。
3 Write Behind Caching Pattern(写后缓存模式,又称写回策略)
写回策略在更新数据时,只更新缓存,而不立即更新数据库。我们的缓存会异步地批量更新数据库。这种设计的优势在于,它能让数据的I/O操作飞快(因为直接操作内存)。由于异步处理,写回策略还能合并对同一数据的多次操作,因此性能提升显著。
这种策略也带来了一些问题。数据不是强一致性的,可能会丢失(比如Unix/Linux非正常关机导致的数据丢失就是这种情况)。写回策略的实现逻辑较为复杂,需要跟踪哪些数据已被更新,需要刷新到持久层。操作系统的写回策略会在cache需要失效时才进行真正持久化,比如内存不足或进程退出等情况,这被称为延迟写入。
写回缓存与写分配
作者:芥末无疆sss
链接:
来源:简书
著作权归作者所有,任何形式的转载请联系作者获得授权并注明出处。
文章通过解析读透、写透和写后缓存(写回)等策略,详细阐述了缓存机制的工作原理和特性。这些策略在数据处理和存储过程中发挥着重要作用,有助于提高数据处理的效率和性能。文章也指出了各种策略可能带来的问题和挑战,为读者提供了全面的了解。
文章来自《钓虾网小编|www.jnqjk.cn》整理于网络,文章内容不代表本站立场,转载请注明出处。