当前位置:网站首页>业务中我们如何更新缓存?Redis
业务中我们如何更新缓存?Redis
2022-08-04 12:07:00 【飞四海】
更新缓存的步骤特别简单,总共就两步:更新数据库和更新缓存。但就这么简单的两步,我们需要考虑好几个问题。
先更新数据库还是先更新缓存?更新缓存时先删除还是直接更新?
假设第一步成功了,第二步失败了怎么办?
假设 2 个线程同时更新同一个数据,A 线程先完成第一步,B 线程先完成第二步,此时该怎么办?
组合一:先更新缓存,再更新数据库(否)
对于这个组合,会遇到这种情况:
假设第 2 步数据库更新失败了,要求回滚缓存的更新,这时该怎么办呢?
我们知道 Redis 不支持事务回滚,除非我们采用手工回滚的方式,先保存原有数据,然后再将缓存更新回原来的数据,这种解决方案就有点尴尬了。
这里我简单举个例子,比如:
原来缓存中的值是 a,两个线程同时更新库存;
线程 A 将缓存中的值更新成 b,且保存了原来的值 a,然后更新数据库;
线程 B 将缓存中的值更新成 c,且保存了原来的值 b,然后更新数据库;
线程 A 更新数据库时失败了,它必须回滚了,那现在缓存中的值更新回什么呢?
要不这样吧,我们在 A 线程更新缓存与数据库的整个过程中,先把缓存及数据库都锁上,确保别人不能更新,这种方法可不可行呢?当然是可行的,但是别人能不能读呢?
假设 A 更新数据库失败回滚缓存时,线程 C 也来参一腿,它需要先读取缓存中的值,这时又返回什么值呢?
看到这个场景,你是不是有点儿印象?不错,这就是典型的事务隔离级别场景。我们只是使用一下缓存而已,你让我自己实现事务隔离级别,这个要求会不会有点高?我们还是考虑别的吧。
组合二:先删除缓存,再更新数据库(否)
使用这种方案,就算我们更新数据库失败了也不需要回滚缓存。这种做法虽然巧妙规避了失败回滚的问题,却引来了 2 个更大的问题。
假设 A 线程先删除缓存,再更新数据库。在 A 线程完成更新数据库之前,后执行的 B 线程反而超前完成了操作,读取 key 发现没数据后,将数据库中的旧值存放到了缓存中。A 线程在 B 线程都完成后再更新数据库,这样就会出现缓存(旧值)与数据库的值(新值)不一致的问题。
为了解决一致性问题,我们可以让 A 线程给 key 加锁,因为写操作特别耗时,这种处理方法会导致大量的读请求卡在锁中。
以上描述的是典型的高可用和一致性难以两全的问题,要再加上分区容错就是 CAP 了,这里我们就不展开讨论了,继续讨论另外两种组合吧。
组合三:先更新数据库,再更新缓存(否)
对于组合三,我们同样需要考虑 2 个问题。
假设第一步成功,第二步失败了怎么办?因为缓存不是主流程,数据库才是,所以我们不会因为更新缓存失败而回滚第一步对数据库的更新。此时,我们一般采取的做法是做重试机制,但重试机制如果存在延时还是会出现数据库与缓存不一致的情况,非常不好处理啊。
假设 2 个线程同时更新同一个数据,A 线程先完成了第一步,B 线程先完成了第二步怎么办?
假设 2 个线程同时更新同一个数据,A 线程先完成了第一步,B 线程先完成了第二步怎么办?这个你先好好想一下。我们接着推演整个过程:A 线程把值更新成 a,B 线程把值更新成 b,此时数据库中的最新值是 b,因为 A 线程先完成了第一步,后完成第二步,所以缓存中的最新值是 a,数据库与缓存的值还是不一致,还是不好处理啊。
因此,我不建议采用以上这个方案。
组合四:先更新数据库,再删除缓存(OK)
针对组合四,我们看看到底会存在哪些问题。
假设第一步成功了,第二步失败了怎么办?这种情况的出现概率与上个组合相比明显少不少,因为删除比更新容易多了。此时虽然它不完美,但出现一致性的问题概率少。
假设 2 个线程同时更新同一个数据,A 线程先完成第一步,B 线程先完成第二步怎么办?这块你也先好好想一下。我们接着推演整个过程:A 线程把值更新成 a,B 线程把值更新成 b,此时数据库中的最新值是 b,因为 A 线程先完成第一步,至于第二步谁先完成已经无所谓了,反正是直接删除缓存数据。
看到这,我们发现组合四完美地解决了以上难题,所以我建议更新缓存时,先更新数据库再删除缓存。
不过,这个解决方案也会引发另外 3个问题。
删除缓存数据后变相出现缓存击穿,此时该怎么办?
删除缓存失败如何重试?
删除缓存失败,重试成功前出现脏数据。这个需要与业务商量,毕竟这种情况还是少见,我们可以根据实际业务情况判断是否需要解决这个瑕疵。毕竟任何一个方案都不是完美的,但如果剩下 1% 的问题需要我们花好几倍的代价去解决,从技术上来讲得不偿失。
边栏推荐
猜你喜欢
ECCV 2022 | Towards Data Efficient Transformer Object Detectors
backbone核心详解系列——RepVGG
数据中台建设(九):数据中台资产运营机制
电源测试之输出动态响应(Output Dynamic Response Test)
论文翻译:2022_Time-Frequency Attention for Monaural Speech Enhancement
项目管理前景
Apache Doris 1.1 特性揭秘:Flink 实时写入如何兼顾高吞吐和低延时
【目标检测】YOLOv4特征提取网络——CSPDarkNet53结构解析及PyTorch实现
Disc burning steps
【HMS core】【FAQ】Account Kit、MDM能力、push Kit典型问题合集6
随机推荐
编辑器vscode Already included file name ‘xxx‘ differs from file name ‘xxx‘ only in casing报错
拦截器,文件流,下载文件?
高速电路PCB布局布线参考
如何用一条命令将网页转成电脑 App
IBM Q复制启动停止查看状态
中电资讯 - 一路“标”升,喜迎Q3开门红
深度学习------戴口罩和不戴口罩
剑指offer专项突击版第19天
飞书更新招聘功能 候选人可选择面试时间
外键约束;外键约束
How to develop small program plug-ins to achieve profitability?
opencv------图片转化为视频
ECCV 2022 | 通往数据高效的Transformer目标检测器
exness:美联储重现鹰派口吻,黄金承压面临转跌信号
【HMS core】【FAQ】Account Kit、MDM能力、push Kit典型问题合集6
中电金信技术实践|分布式事务简说
Leetcode brush questions - 543. Diameter of binary trees, 617. Merging binary trees (recursive solution)
深度学习------pytorch实现划拳模型训练
asp.net解决大文件断点续传
移动跨端技术方案分析对比