缓存一致性问题
起因
源于今天的面试,发现缓存一致性答得不是很好。就查了下相关的博客。
组合方式
1、先更新数据库,再更新缓存;
2、先删除缓存,再更新数据库;
3、先更新数据库,再删除缓存;先删除缓存,再更新数据库;
4、先更新缓存,再更新数据库;(不会吧)
先更新数据库,再更新缓存
不推荐
原因1:线程 安全角度
- 线程A更新了数据库;
- 线程B更新了数据库;
- 线程B更新了缓存;
- 线程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

浙公网安备 33010602011771号