缓存一致性解决方案研究

本文探讨了四种常见的缓存与数据库一致性更新方案,分析了各自的优缺点,重点介绍了延迟双删策略和先淘汰缓存再写数据库的方法,强调了设置缓存过期时间的重要性。推荐在读多写少场景下考虑更新缓存,而在并发写操作频繁时选择删除缓存以避免脏数据问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:如何保证缓存和数据库的一致性

方案

常见更新缓存方式的几种方案:

  1. 先更新缓存,再更新数据库
  2. 先更新数据库,再更新缓存
  3. 先删除缓存,再更新数据库
  4. 先更新数据库,再删除缓存

方案分析

方案一:先更新缓存,再更新数据库

最不可取的方案,因为当缓存更新成功,但是数据库更新失败时(往往数据库更新失败时,会有一些补救方法),会出大问题。

方案二:先更新数据库,再更新缓存

俗称双写,存在的问题:并发更新数据库场景下,会将脏数据刷到缓存。
场景:
更新数据库操作1 ----》更新数据库操作2—》更新缓存操作2—》更新缓存操作1

方案三:删除缓存,更新数据库

这个问题也比较明显,并发情况下,在更新数据库之前,先来了读请求,那么缓存里还是旧数据。

方案四:更新数据库,再删除缓存

不太可能出现的风险:先读后更新,读数据时缓存正好已经过期,去读数据库并读到数据但是还未返回,此时更新数据库完成,并且完成删除缓存操作,这时候前面的读操作才来写缓存,则此时缓存是脏数据。
因为数据库读操作的速度要远快于写操作,所以很难出现读数据库慢于写数据库的情况。

方案对比

方案1和方案2的共同缺点:

并发更新数据库场景下,会将脏数据刷入缓存,但并发写的场景概率相对较小一些,但是总是有可能存在。

方案3和方案4的共同缺点:
  • 主从延时问题:不管先删除还是后删除,数据库主从延时都可能导致脏数据的产生(一般都是写主,读从)
  • 删除缓存失败,都会产生脏数据
更新还是删除缓存?
  1. 更新缓存需要有一定的维护成本,并且会存在并发更新的问题
  2. 写多读少的情况下,频繁跟新缓存,缓存意义何在
  3. 有些场景下,缓存值是经过复杂计算的,每次都更新,会频繁计算写入,浪费性能

而删除的优点:简单、成本低,容易开发;缺点:会造成一次cache miss(这里曾经踩过坑,使用redis的 list结构 缓存一个有排序的数据,设置了过期时间,每次读取都是全部数据,因此,没有pop操作只有push。预想流程为,redis有,读redis,只有redsi过期,再去读数据库写入。出现故障:一段时间后,发现机器内存打满崩了,redis把内存占完了,就是这个key数据过多造成的。分析原因:1、当过期或者没有缓存的时候,大量读请求进来,一起没读到,一起去读数据库,读完一起返回,写入缓存,因为只有push,就撑了太多数据;2、不明原因读缓存没读到,就读数据库,然后push,并更新了时间,如果存在某种固定错误,则会一直push,过不了期。但是这个问题为什么在测试环境没有复现就很奇怪)。

如果更新缓存开销较小并且读多写少,基本不会有写并发的时候,可以用更新缓存,否则建议使用删除缓存。

总结

方案问题问题出现概率推荐程度
先更新缓存,再更新数据库更新数据库失败,将导致数据错误大概率出现不推荐
先更新数据库,再更新缓存并发场景下,易出现脏数据情况概率一般不推荐
先删除缓存,再更新数据库数据库更新前的读操作,会导致脏数据概率较大不推荐
先更新数据库,再删除缓存特殊情况下,写数据库快于读数据库,则会出现脏数据概率较小依据场景可使用

推荐方案

延迟双删

采用更新前后双删除策略

  • 先淘汰缓存
  • 再写数据库
  • 休眠定时,再次淘汰缓存

双删是为了确保并发读请求结束后,写请求能够删除读请求造成的脏数据,具体的休眠时间,应该结合业务逻辑耗时来定。第二次的删除可做异步处理,添加删除失败重试机制。

注意

关于缓存过期时间的问题,如果设置了过期时间,则不一致情况,可能只是暂时的,而如果没有设置过期时间,那不一致问题只有在下一次更新时,才有可能解决,所以一定要设置缓存过期时间,哪怕时间很长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值