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