从理论到实践,深入解析Redis数据一致性问题(一)
mhr18 2024-12-04 13:36 19 浏览 0 评论
1.数据一致性问题
对于数据库与缓存数据的一致性问题相信对于大部分开发人员来说并不陌生,也经常遇到。实际上,只要是我们使用到缓存,那么必然就会产生数据库与缓存间的数据不一致的问题。
既然是“必然产生数据不一致性问题”,那么我们在设计程序时就需要考虑这些问题:系统是否必须要求数据库与缓存间数据达到完全一致性(即强一致性)?系统是否能够接受一定时间下的数据不一致性,能够接受多少时间下的数据不一致性?
带着上面的问题,我们又可以将“一致性”分为如下两种形式:
1)强一致性
它要求数据库与缓存间数据要达到完全一致性。要达到强一致性,就只能通过加锁将请求变成串行化执行,但这样系统的吞吐量也就大大降低了。很显然,这样并不是设计缓存的初衷,实际上也没有必要要求强一致性。
2)弱一致性
也称为最终一致性,它能接受一定时间内的数据库与缓存间数据存在短暂的不一致性性,只要求最终的数据是一致的即可。这样既提高了系统的并发性与吞吐量,也能够保障了数据最终的一致性。
了解完上述的“一致性”问题,那么在什么情况下会导致数据库与缓存间数据的不一致问题呢?简单来讲,主要就是“并发请求与更新问题”与“更新/删除异常问题”两个问题所导致的,具体场景如下四种情况。
1.1 先更新数据库,再更新缓存
这种场景要求数据更新时,先更新数据库,待数据库更新成功之后,再更新缓存数据,如图1所示:
如图1所示,很显然,这种方式存在如下两个问题:
- 1)如果数据库先更新成功了,还未对Redis缓存进行更新的间隙期间(即这个间隙期间数据库是更新后的新数据,而Redis缓存是更新前的旧数据)。如果在这个间隙期间有新的数据读取请求过来,则读到的都是Redis缓存的更新前的旧数据。
- 2)如果数据库先更新成功了,再次更新Redis缓存时,数据更新失败了(即更新异常,这个时候数据库是更新后的新数据,而Redis缓存是更新前的旧数据)。如果这种更新异常情况发生,那么后面的所有数据读取请求读到的都是Redis的更新前的旧数据,这种情况可能要持续到缓存过期失效之后,才能请求到正确的数据。
1.2 先更新缓存,再更新数据库
这种场景要求数据更新时,先更新缓存,待缓存更新成功之后,再更新数据库,如图2所示:
如图2所示,这种方式相对于“先更新数据库,再更新缓存”看似可以解决读取旧数据的问题,但依然存在如下两个问题:
- 1)如果Redis缓存先更新成功了,还未对数据库进行更新的间隙期间(即这个间隙期间数据库是更新前的旧数据,而Redis缓存是更新后的新数据),这个间隙期间会导致数据的不一致问题出现;
- 2)如果Redis缓存先更新成功了,再次更新数据库时,数据更新失败了(即更新异常,这个时候数据库是更新前的旧数据,而Redis缓存是更新后的新数据),这同样会导致数据的不一致问题出现。
面对上面两种数据不一致的情况,如果这个时候有数据读取请求过来,那么读取到的都是Redis缓存未生效的新数据。这种胀读Redis缓存未生效的新数据会导致系统出现严重的后果,如遇到关联查询或者关联业务操作都会面临不可预知的一系列错误。
1.3 先更新数据库,再删除缓存
这种场景要求数据更新时,先更新数据库,待数据库更新成功之后,再删除缓存数据,如图3所示:
如图3所示,它看似可以解决“并发更新”的问题,但依然会存在下面的两个问题:
- 1)如果数据库先更新成功了,还未对Redis缓存数据进行删除的间隙期间(即这个间隙期间数据库是更新后的新数据,而Redis缓存是更新前的旧数据)。如果在这个间隙期间有新的数据读取请求过来,则读到的都是Redis缓存的更新前的旧数据。
- 2)如果数据库先更新成功了,再次删除Redis缓存数据时,数据删除失败了(即更新异常,这个时候数据库是更新后的新数据,而Redis缓存是更新前的旧数据)。如果这种删除异常情况发生,那么后面的所有数据读取请求读到的都是Redis的更新前的旧数据,这种情况可能要持续到缓存过期失效之后,才能请求到正确的数据。
1.4 先删除缓存,再更新数据库
这种场景要求数据更新时,先删除缓存数据,待缓存数据删除成功之后,再更新数据库,如图4所示:
如图4所示,相对于“先更新数据库,再删除缓存”,这种方式在没有高并发的情况下,是有可能保持数据一致性的。它解决了如下两个问题:
- 1)如果Redis缓存先删除成功了,还未对数据库进行更新的间隙期间,此间隙期间的数据读取请求查询不到任何数据,也就不存在数据一致性的问题;
- 2)如果Redis缓存先删除成功了,再次更新数据库时,数据更新失败了。同样,之后的数据读取请求查询不到任何数据,也就不存在数据一致性的问题。
尽管如此,但如果是处于读写并发的情况下,还是会出现数据不一致的情况,如图5所示:
如图5所示:
- 1)请求A的数据写请求先执行将Redis缓存数据删除,删除成功后,但由于网络延迟原因,还没有来得及执行写数据库操作;
- 2)在此间隙期间,请求B的数据读请求开始查询Redis缓存,发现Redis缓存没数据。接下来再继续请求查询数据库,数据库中有原来的旧数据。请求B获取数据库中原来的旧数据,并同时将其更新到Redis缓存中;
- 3)此时,请求A网络延迟结束,把新数据写入数据库。这时,导致数据库与Redis缓存出现数据不一致问题。
其实,面对上面这些场景所导致的数据不一致性问题,在实际开发中没有十分完美的解决方案,只有根据自己的应用场景找到最适合方案。
一般使用场景简单且并发较低的情况,解决这些数据不一致性问题可以使用“传统的单删(即先删除缓存,再更新数据库)或者延时双删”就可以满足其要求;如果使用场景复杂、对并发要求较高的场景下,则就需要选择“消息队列异步重试、定时任务异步重试、Binlog日志订阅”等方案。
本文只阐述“延时双删”策略,其他方案待下一篇文章继续展开讨论。
2.延时双删方案
在解决上面的单删(即先删除缓存,再更新数据库)所面临的“在读写并发进行时,会产生缓存是旧数据,而数据库是新数据”的数据不一致问题,我们就可以使用延时双删方案来进行解决。
顾名思义,延时双删就是指:在进行数据更新时,先删除Redis缓存中的数据,然后更新数据库的值。在更新完数据库值之后,我们可以让线程先休眠 一小段时间后再进行一次Redis缓存数据删除操作。有了休眠的这段时间,即使有其他线程从数据库中读取到旧的数据并重新更新到Redis缓存中,我们也能够将其再次删除,以保证Redis缓存中会是新的值。
如图6所示:
现在,通过延时双删方案,我们再来分析上面图5的示例:
- 1)请求A的数据写请求先执行将Redis缓存数据删除,删除成功后,但由于网络延迟原因,还没有来得及执行写数据库操作;
- 2)在此间隙期间,请求B的数据读请求开始查询Redis缓存,发现Redis缓存没数据。接下来再继续请求查询数据库,数据库中有原来的旧数据。请求B获取数据库中原来的旧数据,并同时将其更新到Redis缓存中;
- 3)此时,请求A网络延迟结束,把新数据写入数据库,完成数据库写入后进入休眠;
- 4)休眠一段时间后,请求A再次删除Redis缓存数据,从而保证了数据的一致性。
对于延时双删方案,这个“延时”到底要延时多久才合适呢?
就如上面的例子,如果请求A再次删除Redis缓存数据太快(即在请求B将数据库中的旧值更新到缓存之前,就已经把缓存删除了),那么这次删除就没任何意义,也解决不了一致性问题。所以,这个休眠时间设置是关键所在。
但不幸的是,这确实很难给定一个准确答案,一般设置原则是“大于缓存的读写时间即可,又或者是大于从数据库读取数据+写入缓存的时间和即可”。实际上,这个时间只又在经过不断的压测和实际环境运行,才能够找到一个合理的预估时间,从而尽可能的去降低发生数据不一致性问题的概率。
相关推荐
- Dubai's AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East's Silicon Gateway
-
AI-generatedimageAsianFin--Dubaiisrapidlytransformingitselffromadesertoilhubintoaglob...
- OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs
-
TMTPOST--OpenAIisescalatingthepricewarinlargelanguagemodel(LLM)whileseekingpartnershi...
- 黄仁勋说AI Agent才是未来!但究竟有些啥影响?
-
,抓住风口(iOS用户请用电脑端打开小程序)本期要点:详解2025年大热点你好,我是王煜全,这里是王煜全要闻评论。最近,有个词被各个科技大佬反复提及——AIAgent,智能体。黄仁勋在CES展的发布...
- 商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署
-
1、本文属于mini商城系列文档的第0章,由于篇幅原因,这篇文章拆成了6部分,本文属于第5部分2、mini商城项目详细文档及代码见CSDN:https://blog.csdn.net/Eclipse_...
- Python+Appium环境搭建与自动化教程
-
以下是保姆级教程,手把手教你搭建Python+Appium环境并实现简单的APP自动化测试:一、环境搭建(Windows系统)1.安装Python访问Python官网下载最新版(建议...
- 零配置入门:用VSCode写Java代码的正确姿
-
一、环境准备:安装JDK,让电脑“听懂”Java目标:安装Java开发工具包(JDK),配置环境变量下载JDKJava程序需要JDK(JavaDevelopmentKit)才能运行和编译。以下是两...
- Mycat的搭建以及配置与启动(mycat2)
-
1、首先开启服务器相关端口firewall-cmd--permanent--add-port=9066/tcpfirewall-cmd--permanent--add-port=80...
- kubernetes 部署mysql应用(k8s mysql部署)
-
这边仅用于测试环境,一般生产环境mysql不建议使用容器部署。这里假设安装mysql版本为mysql8.0.33一、创建MySQL配置(ConfigMap)#mysql-config.yaml...
- Spring Data Jpa 介绍和详细入门案例搭建
-
1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...
- 量子点格棋上线!“天衍”邀您执子入局
-
你是否能在策略上战胜量子智能?这不仅是一场博弈更是一次量子智力的较量——量子点格棋正式上线!试试你能否赢下这场量子智局!游戏玩法详解一笔一画间的策略博弈游戏目标:封闭格子、争夺领地点格棋的基本目标是利...
- 美国将与阿联酋合作建立海外最大的人工智能数据中心
-
当地时间5月15日,美国白宫宣布与阿联酋合作建立人工智能数据中心园区,据称这是美国以外最大的人工智能园区。阿布扎比政府支持的阿联酋公司G42及多家美国公司将在阿布扎比合作建造容量为5GW的数据中心,占...
- 盘后股价大涨近8%!甲骨文的业绩及指引超预期?
-
近期,美股的AI概念股迎来了一波上升行情,微软(MSFT.US)频创新高,英伟达(NVDA.US)、台积电(TSM.US)、博通(AVGO.US)、甲骨文(ORCL.US)等多股亦出现显著上涨。而从基...
- 甲骨文预计新财年云基础设施营收将涨超70%,盘后一度涨8% | 财报见闻
-
甲骨文(Oracle)周三盘后公布财报显示,该公司第四财季业绩超预期,虽然云基建略微逊于预期,但管理层预计2026财年云基础设施营收预计将增长超过70%,同时资本支出继上年猛增三倍后,新财年将继续增至...
- Springboot数据访问(整合MongoDB)
-
SpringBoot整合MongoDB基本概念MongoDB与我们之前熟知的关系型数据库(MySQL、Oracle)不同,MongoDB是一个文档数据库,它具有所需的可伸缩性和灵活性,以及所需的查询和...
- Linux环境下,Jmeter压力测试的搭建及报错解决方法
-
概述 Jmeter最早是为了测试Tomcat的前身JServ的执行效率而诞生的。到目前为止,它的最新版本是5.3,其测试能力也不再仅仅只局限于对于Web服务器的测试,而是涵盖了数据库、JM...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Dubai's AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East's Silicon Gateway
- OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs
- 黄仁勋说AI Agent才是未来!但究竟有些啥影响?
- 商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署
- Python+Appium环境搭建与自动化教程
- 零配置入门:用VSCode写Java代码的正确姿
- Mycat的搭建以及配置与启动(mycat2)
- kubernetes 部署mysql应用(k8s mysql部署)
- Spring Data Jpa 介绍和详细入门案例搭建
- 量子点格棋上线!“天衍”邀您执子入局
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (56)
- oracle 乐观锁 (53)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)