缓存和数据库一致性

问题分析

  1. 当有数据需要更新的时候,先更新缓存还是先更新数据库?如何确保更新缓存和更新数据库这两个操作的原子性?

  2. 更新缓存的时候该怎么更新?修改还是删除?

缓存模型-Cache-Aside

Cache-Aside,中文也叫旁路缓存模式,该模式能够尽可能的解决缓存与数据库数据不一致的问题。

Cache-Aside 分为读缓存和写缓存两种情况:

读缓存

  1. 读取数据。
  2. 检查缓存中是否有需要的数据,如果命中缓存(Cache Hit),则直接返回数据。
  3. 如果没有命中缓存,即 Cache Miss,那么就先去访问数据库。
  4. 将从数据库中读取到的数据设置到缓存中。
  5. 返回数据。

写缓存

先更新数据库再删除缓存,还是先删除缓存再更新数据库,Cache-Aside推荐更新数据库再删除缓存

  1. 写缓存
  2. 更新数据库
  3. 删除旧缓存

先更新数据库再删除缓存 VS 先删除缓存再更新数据库

假设有 A、B 两个并发请求:

  • 先更新数据库再删除缓存:当请求 A 更新数据库之后,还未来得及进行缓存清除,此时请求 B 查询到并使用了 Cache 中的旧数据。
  • 先删除缓存再更新数据库:当请求 A 执行清除缓存后,还未进行数据库更新,此时请求 B 进行查询,查到了旧数据并写入了 Cache。

方案一:先删除缓存再更新数据库+延迟双删

先执行缓存清除操作,再执行数据库更新操作,延迟 N 秒之后再执行一次缓存清除操作,这样就不用担心缓存中的数据和数据库中的数据不一致了。
那么这个延迟 N 秒,N 是多大比较合适呢?一般来说,N 要大于一次写操作的时间,如果延迟时间小于写入缓存的时间,会导致请求 A 已经延迟清除了缓存,但是此时请求 B 缓存还未写入,具体是多少,就要结合自己的业务来统计这个数值了。
缺点:延迟时间N很难评估

方案二:先更新数据库再删除缓存+消息队列

但是更新数据库和删除缓存毕竟不是一个原子操作,要是数据库更新完毕后,删除缓存失败了咋办?
对于这种情况,一种常见的解决方案就是使用消息中间件来实现删除的重试。消息队列一般都自带消费失败重试的机制,当我们要删除缓存的时候,就往 MQ 中扔一条消息,缓存服务读取该消息并尝试删除缓存,删除失败了就会自动重试。
优点:消息队列保证可靠性:写到队列中的消息,成功消费之前不会丢失(重启项目也不担心)

消息队列保证消息成功投递:下游从队列拉取消息,成功消费后才会删除消息,否则还会继续投递消息给消费者(符合我们重试的场景)

方案三:先更新数据库再删除缓存+订阅变更日志

订阅变更日志,目前也有了比较成熟的开源中间件,例如阿里的 canal,使用这种方案的优点在于:

无需考虑写消息队列失败情况:只要写 MySQL 成功,Binlog 肯定会有

自动投递到下游队列:canal 自动把数据库变更日志「投递」给下游的消息队列

当然,与此同时,我们需要投入精力去维护 canal 的高可用和稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VI仔爱学习

让我看看是谁在学习

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值