缓存一致性问题

起因

源于今天的面试,发现缓存一致性答得不是很好。就查了下相关的博客。

组合方式

1、先更新数据库,再更新缓存;
2、先删除缓存,再更新数据库;
3、先更新数据库,再删除缓存;先删除缓存,再更新数据库;
4、先更新缓存,再更新数据库;(不会吧)

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

不推荐

原因1:线程 安全角度

  1. 线程A更新了数据库;
  2. 线程B更新了数据库;
  3. 线程B更新了缓存;
  4. 线程A更新了缓存;
    容易产生脏数据

原因2: 业务场景角度

1、写多读少的场景,, 缓存频繁更新
2、写入 数据库的值,并不是直接写入缓存,而是需要经过一系列的计算 在写入缓存。 浪费性能。

** 发现个问题,我们项目中好像用的就是这种模式!!!My God**

到项目中细想了下, 在我们业务场景中 使用时 saveToDB 后,在 saveRedis,缓存10min中,
1、会存在 并发写的时候,且第二次更新缓存失败,会出现读到老数据(这种概率极小,场景中可以完全忽略)
2、我们的业务场景,是一次任务,会 进行一次落库操作,每次创建都会生成不同的任务,所以基本不存在上述的问题。
3、同时在保存数据后,保存到redis的数据是 未经过处理的数据(这次其实可以不需要),然后 会对 未经过处理的数据 用 velocity 渲染,生成不同的版本的结果数据, 在 保存到 redis, 这是 客户 最终要 获取的结果。为了保证在大量用户获取结果的时候,减少mysql的压力。

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

操作

请求A进行写操作,删除缓存;
请求B查询发现缓存不存在;
请求B去数据库查询得到旧值;
请求B将旧值写入缓存;
请求A将新值写入数据库;

导致缓存中是老数据

方案:延时双删 策略。
延迟一定时间后(一般ms级别,根据业务判断),再次删除

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

Cache Aside Pattern

+ 查询: 先查询缓存,缓存有直接返回;缓存没命中,查询数据库,成功,设置缓存
+ 命中: 命中缓存,直接返回
+ 更新: 更新数据库成功后,删除缓存

产生的并发操作:

并发 一个查询, 一个 写操作
查缓存, 查数据库
写数据, 删缓存
组合排序
写数据库,读操作(老数据), 删缓存 -- 正常,不会一直 都是 老数据
写数据库,删缓存,读操作 ---正常
读数据,写数据库,删缓存 -- 正常
读数据库, 写数据库,删缓存,更新缓存 -- 脏数据

针对 一个查询操作,一个 更新操作的 并发。

  • 如果先更新了数据中的数据,此时 并发查询拿到的是 old 数据,
  • 更新操作后,删除缓存,后续的查询 就 查到更新的 值。

有个概率很小的情况

  • 一个读操作,没有命中缓存,去 数据库中取数据
  • 一个写操作,写完数据库后,让缓存失效, 然后 读操作把老数据 放进缓存。造成脏数据。

更新缓存失败怎么办?

方案一: 在业务代码中 使用 mq 尝试重试

方案二: 通过订阅 binlog,在 非业务代码中进行 重试

参考文献:

https://blog.csdn.net/xlgen157387/article/details/80389101
https://coolshell.cn/articles/17416.html

posted @ 2020-04-24 22:24  小烽  阅读(506)  评论(0)    收藏  举报