Cache-Aside

应用代码需要首先在缓存中查找数据,如果未找到,再从数据库中加载数据,并将其添加到缓存中。

当应用程序需要修改 DB 的数据,只需要先更新 DB,再删除 Cache

优点是只缓存需要的数据,减少了缓存资源浪费。

缺点是可能会有缓存穿透问题,导致无效的数据库访问增多。

这种模式在业务上经常使用

Read-Through

缓存与数据存储配合紧密,应用代码在读取数据时,如果缓存中不存在,缓存会自动负责从数据库加载数据并返回给客户端。

优点是对应用透明,使得应用代码更为简洁。

缺点是可能在初次访问数据时有延迟。

Write-Through

应用在更新数据时,会 同时写入缓存和数据库(分布式事务),确保缓存数据的一致性。

优点是数据最新,缓存始终反映了数据库的当前状态。

缺点是可能会增加写入的延迟。

Write-Back

应用写操作 只更新缓存,不立即写入数据库;数据会在特定条件下(如定时或批量)异步写入数据库

优点是能极大提高写入性能。

缺点是存在数据丢失的风险,如果缓存在数据同步到数据库前出现故障。

如何更新数据?

强一致性

如果要强一致性保障,那就只能用 Write-Through + 分布式事务

纯粹的 Write-Through 是无法保障强一致性,只能最终一致

要想保证强一致性,就必须将 DB 的更新操作和 Cache 的更新操作整合成一个分布式事务

最终一致性

如果不要求强一致性,可以使用 先更新 DB,再删除缓存 的方案,并辅以 补偿机制

这种思想很简单:要更新数据,那就更新数据库呗,由于数据库的数据已经发生了变化,那么缓存的数据肯定就没有意义了,删除掉即可

下一次读取数据,在客户端看来就是 cache miss,重建缓存即可

但是存在一个问题:如果缓存删除失败,那么后续的请求就会读取到缓存的过期数据,就产生了不一致

删除失败,重试不就好了,一般实现是将删除操作扔到消息队列,由消费者异步删除缓存:

  • 如果删除成功,提交 offset
  • 否则不提交 offset,下一次消费的消息还是这一条,就实现了重试

这种方法保证的是最终一致性,在 DB 更新完成到成功删除缓存期间,是有数据不一致的情况的(但时间通常很短)

题外话

不应该在如何实现缓存与 DB 的一致性这条道路上走火入魔

想想自己为什么使用缓存?

使用缓存的目的通常应该是:缓存 不经常 修改的数据,减少对 DB 的请求,降低 DB 压力,保证系统的可用性

当然,也有 Write-Back 这种模式,缓存经常修改的数据,避免频繁写 DB

具体可以看看这篇知乎上的高赞回答:Redis 双写一致性,先改数据库再改缓存可以吗?有什么问题?

要不要用缓存?应该用哪种模式?是需要结合实际需求来确定的,数据一致性重要嘛?当然重要,但是你要考虑自己的业务到底有没有这么高的一致性需求,如果真的有很高的一致性要求,并且还经常修改数据,个人认为这种场景还不如直接用 DB