缓存一致性问题的解决策略

缓存一致性问题的背景和概念介绍

在一个系统中,我们通常使用数据库来存储数据,以保证数据的持久性。但是,由于数据库的读写速度相对较慢,如果每次请求都直接访问数据库,会降低系统的响应速度。为了提高系统的性能,我们通常会缓存数据库中的数据。在项目中,我们需要同时维护数据库和缓存的数据确保它们是一致的,但在多线程的场景下,会有各种各样的情况导致数据库和缓存不一致的问题。

应用场景和例子

情景一:更新操作先更新数据库,再修改缓存。

&&

情景二:更新操作先修改缓存,再更新数据库。

假设有线程1和线程2,整个程序按照时间顺序执行四步,对于情景一,假设当前数据库和缓存中的数据都是1,第一步线程1把数据库更新为2,此时缓存还没有更新;第二步切换到线程2执行,线程2把数据库更新为3;第三步线程2把缓存修改为3;第四步又切换到线程1执行,线程1执行之前未完成的工作,把缓存修改为2; 此时数据库中的数据为3,而缓存中的数据为2,出现不一致问题,对于情景二同理。

 并且这样会使逻辑变得复杂,在更新的时候首先要判断缓存中是否有对应的数据,如果不存在对应的数据是不能更新的,因为这样会把很多没必要的数据塞到内存中,造成内存浪费,同时还要去保证判断和写操作之间的原子性。同时生成缓存数据的成本可能较高,如果每次更新操作都要去修改缓存的话,对于网红大V的粉丝数,写操作是远远大于读操作的,如果每次粉丝数增加都要去修改缓存,成本就很大了。

所以这里的解决方案是当数据库的数据需要更新时,直接删除缓存。而需要读取数据时,才去更新缓存,这样就引出了情景三和情景四。

情景三:更新操作先删除缓存,再更新数据库。

&&

情景四:更新操作先更新数据库,再删除缓存。

对于这两种情景,我们选择先更新数据库,再删除缓存,原因如下图。结合下图来看文字,注意只有缓存未命中的时候,才会去读数据库更新缓存,否则就直接返回缓存中读到的数据。

假设线程一先删缓存,再更新数据库,在这两个操作之间,线程二去读取数据,因为缓存已经被删了所以缓存未命中,从而去数据库读,但是此时线程一还没有完成更新数据库的操作,所以此时线程二读到的是旧数据,然后它更新了缓存。线程二执行完成后,线程一继续执行,线程一更新数据库,此时就存在了数据不一致问题,当前数据库中的数据是线程一新写的,而缓存中的数据是线程二在之前更新的。那么从现在开始到缓存过期的这段时间,这个数据都是不一致的。

而对于先更新数据库,再删除缓存,如上图,只有当线程二的读取数据操作发生在线程一的写库和删缓存操作之间,此时线程一已经写库完成了,但是线程二读到的是之前的缓存,就存在不一致问题,不过在线程一写库完成,线程一删除缓存这两个操作之间的时间是远小于线程一删除缓存,线程一写库完成这两个操作之前的时间的,所以即使出现缓存不一致的情况,概率也远低于先删除缓存,再更新数据库的情景。

最后解法:更新操作先更新数据库,再删除缓存,并引入延时双删。

不过推进到现在仍然存在缓存不一致的情况,那么就可以引入一种叫做延时双删的策略,具体做法是,在更新数据库后,先删除缓存中的数据,并设置一个短暂的延时。在延时结束后,再次删除缓存中的数据,这样就算另一个线程把读到的旧数据写入缓存中,因为有个二次删除的操作,缓存不一致的时间也会大大降低。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/493067.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud学习

1、什么是SpringCloud Spring cloud 流应用程序启动器是基于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成。Spring cloud Task,一个生命周期短暂的微服务框架,用于快速构建执行有限数据处理的应用程序。Spring cloud 流应用程…

2.26 Qt day4+5 纯净窗口移动+绘画事件+Qt实现TCP连接服务+Qt实现连接数据库

思维导图 Qt实现TCP连接 服务器端&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer>//服务器端类 #include<QTcpSocket>//客户端类 #include<QMessageBox>//消息对话框类 #include<QList>//链…

java农产品商城商城计算机毕业设计包运行调试讲解

jsp mysql农业商城 特效&#xff1a;js产品轮播 功能&#xff1a; 前台&#xff1a; 1.绿色水果 图文列表 详情 2.新闻动态 文章标题列表 详情 3.有机蔬菜 图文列表 详情 4.有机谷物 图文列表 详情 5.有机大米 图文列表 详情 6.用户注册 登陆&#xff08;选择用户和管…

EXCEL如何从另一个表查找匹配信息

目录 1.背景&#xff1a;我们有一个目标呈现表&#xff0c;想要从另一个表中查询得到信息&#xff0c;比如根据身份证id查询该id的名字、性别等个人基本信息&#xff0c;或者从另一个财务信息表查询该id的工资信息等&#xff1b; 2.基础方法&#xff1a;利用VLOOKUP函数根据单…

Windows部署WebDAV服务并映射到本地盘符实现公网访问本地存储文件

文章目录 前言1. 安装IIS必要WebDav组件2. 客户端测试3. 使用cpolar内网穿透&#xff0c;将WebDav服务暴露在公网3.1 安装cpolar内网穿透3.2 配置WebDav公网访问地址 4. 映射本地盘符访问 前言 在Windows上如何搭建WebDav&#xff0c;并且结合cpolar的内网穿透工具实现在公网访…

【Spring Boot 源码学习】深入 BootstrapContext 及其默认实现

《Spring Boot 源码学习系列》 深入 BootstrapContext 及其默认实现 一、引言二、往期内容三、主要内容3.1 BootstrapContext3.1.1 源码初识3.1.2 get 方法3.1.3 getOrElse 方法3.1.4 getOrElseSupply 方法3.1.5 getOrElseThrow 方法3.1.6 isRegistered 方法 3.2 ConfigurableB…

linux常用的网络命令实战分享

文章目录 ifup/down命令ifconfig命令观察网络接口信息修改接口参数增加虚拟网络接口 route命令查看路由表增加路由表规则删除路由表规则 IP 命令ip linkip addr设定路由 ip route arp 命令 在实际研发运维工作中常常会涉及到网关相关的操作和知识&#xff0c;这里对linux下常用…

探索C语言位段的秘密

位段 1. 什么是位段2. 位段的内存分配3. 位段的跨平台问题4. 位段的应用4. 使用位段的注意事项 1. 什么是位段 我们使用结构体实现位段&#xff0c;位段的声明和结构体是类似的&#xff0c;有两个不同&#xff1a; 位段的成员必须是int&#xff0c;unsigned int&#xff0c;或…

蓝桥杯-星期一

思考:计算到底一开始有多少天&#xff0c;再以7天为周期算&#xff0c;然后进行除&#xff0c;唯一要确定可能是他一开始1.1号是星期几&#xff0c;但是此题不影响。 我还没想明白如果算出1.1号是星期一是否有影响。 #include <iostream> using namespace std; int mai…

php脚本输出中文在浏览器中显示乱码

问题说明 这个问题一般出现在较低版本的php中&#xff0c;原因是php和浏览器的字符解析方式不对应 &#xff0c;导致中文字符被错误解析成乱码 &#xff08;注&#xff0c;此处的php版本任意切换是依赖于小皮面板&#xff08;phpstudy&#xff09;实现的&#xff0c;感兴趣可以…

vcomp140.dll丢失怎样修复(最新教程)

打开软件游戏时候出现找不到vcomp140.dll&#xff1a;或vcomp140.dll丢失是怎么回事&#xff1f;今天我尝试打开软件就出现这个问题&#xff0c;今天我把解决这个问题的方法分享给大家。 一、电脑丢失vcomp140.dll文件打开软件都是提示什么 当电脑丢失vcomp140.dll文件时&…

react useMemo 用法

1&#xff0c;useCallback 的功能完全可以由 useMemo 所取代&#xff0c;如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。 usecallback(fn,inputs)is equivalent to useMemo(()> fn, inputs). 区别是:useCallback不会执行第一个参数函数&#xff0c;而是将它返…