缓存

66 阅读2分钟

zhuanlan.zhihu.com/p/408515044

「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。

如何保证两步都执行成功? 重试

那这是不是意味着,只要执行失败,我们「无脑重试」就可以了呢?

答案是否定的。现实情况往往没有想的这么简单,失败后立即重试的问题在于:

  • 立即重试很大概率「还会失败」
  • 「重试次数」设置多少才合理?
  • 重试会一直「占用」这个线程资源,无法服务其它客户端请求

看到了么,虽然我们想通过重试的方式解决问题,但这种「同步」重试的方案依旧不严谨。

那更好的方案应该怎么做?

答案是:异步重试。 把重试请求写到「消息队列」中,然后由专门的消费者来重试,直到成功。 或者更直接的做法,为了避免第二步执行失败,我们可以把操作缓存这一步,直接放到消息队列中,由消费者来操作缓存。

到这里你可能会问,写消息队列也有可能会失败啊?而且,引入消息队列,这又增加了更多的维护成本,这样做值得吗?

这个问题很好,但我们思考这样一个问题:如果在执行失败的线程中一直重试,还没等执行成功,此时如果项目「重启」了,那这次重试请求也就「丢失」了,那这条数据就一直不一致了。

所以,这里我们必须把重试消息或第二步操作放到另一个「服务」中,这个服务用「消息队列」最为合适。这是因为消息队列的特性,正好符合我们的需求:

image.png

那如果你确实不想在应用中去写消息队列,是否有更简单的方案,同时又可以保证一致性呢?

方案还是有的,这就是近几年比较流行的解决方案:订阅数据库变更日志,再操作缓存

image.png

主从库延迟和延迟双删问题

线程 A 可以生成一条「延时消息」,写到消息队列中,消费者延时「删除」缓存。