一文总结缓存与数据一致性问题

缓存与数据一致性

先看一个大概的流程图

根据以上的流程没有问题,但是当数据变更的时候,如何把缓存变到最新,使我们下面要讨论的问题。

1.更新了缓存,再更新数据库

假设数据库更新成功,缓存更新失败,在缓存失效和过期的时候,读取到的都是老数据缓存。

2.更新了数据库,再更新缓存

缓存更新成功了,数据库更新失败,是不是读取的缓存的都是错误的

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

有一定的使用量。即使数据库更新失败。缓存也可以会刷。

在高并发的场景下,存在的问题是什么?

假设A请求删除了缓存数据,B请求来获取数据,查询不到数据时,认为是数据过期了,直接拿数据库内的数据并且填充到缓存了。这时A请求才去更新数据库,而缓存数据已经被B拿旧数据填充了。这样缓存和数据库就不是一致的了。

可以采用延迟双删的策略,应对这个高并发下出现的问题,那什么是延迟双删呢?

这里指的是删除两次缓存,首先删除缓存,然后更新数据库,为了避免有其他的请求拿错误的数据覆盖到了缓存,再次删除掉缓存,让下一次的请求命中到数据库,从而把最新的数据填充到缓存。

为什么会存在延迟双删呢,普通的双删时,假设B请求获取到了旧数据,准备填充到缓存,A请求刚刚更新完数据库,立刻删除了缓存,在删除完成后B请求才把旧数据去填充,这样依然会出现缓存与数据库不一致的情况(即缓存内数据错误)。

延迟双删就是A请求更新完数据库之后,延迟那么一会再去删除缓存,这样的目的也很明显,就是为了让B请求(以及其他很多相近时间的请求)已经拿旧数据填充过缓存了,并且已经走完这一段逻辑了,后续不会去尝试覆盖缓存了。这个时候再去删除缓存,下一次去填充缓存的时候就拿到的是A请求更新好的正确的数据了。

4.先更新数据库,再更新缓存

假设A请求更新了数据库,还未情况缓存的情况下,B请求拿到的是缓存内的旧数据,因为A还没来得及把新的数据填充到缓存。

存在的问题就是:会有一段时间存在缓存滞后的问题,但是在没有实时一致性那么强要求的场景是最好的方案了

但是这里还有问题,在更新数据库后,想要去更新缓存的时候,因为一些未知的问题,导致缓存没有更新成功,这个情况怎么办?缓存里面就会一直是旧数据。具体看一下第五点讲一下拓展思路

5.扩展思路

第四点的缓存更新失败了,导致缓存与数据不一致,我们想要应对这种问题

我称这种问题叫如何保证缓存与数据的最终一致性?

  • 引入消息队列做补偿进行兜底

    删除失败的缓存,作为消息打入 mq,mq 消费者进行监听,再次进行重试刷缓存。

  • 阿里开源的cannal

    监听数据库的变化,做一个公共服务,专门来对接缓存刷新。优点业务解耦,业务太多冗余代码复杂度。

    canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送 dump 协议,其实就是 MySQL 的主从复制的原理

相关推荐
猿java13 分钟前
程序员,你使用过灰度发布吗?
java·分布式·后端
iOS开发上架哦13 分钟前
Flutter,让我们把 Navigator与Route详解 再讲一遍
后端
半桔13 分钟前
红黑树剖析
c语言·开发语言·数据结构·c++·后端·算法
我的div丢了肿么办13 分钟前
vue3第二次传递数据方法无法获取到最新的值
前端·面试·github
疯狂的程序猴14 分钟前
flutter - 图文讲解表单组件基本使用 & 注册实战
后端
星星电灯猴15 分钟前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
后端
Asthenia041216 分钟前
深入剖析 MyBatis-Plus 自动注入封装的实现原理及其创新
后端
佩奇快跑17 分钟前
使用 Redis Stream 解决 Java 与 Python 的长连接请求交互
后端
eason_fan22 分钟前
前端面试手撕代码(字节)
前端·算法·面试
加瓦点灯25 分钟前
当你的对象结构拒绝修改时,访问者模式是如何破局的?
后端