百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

从理论到实践,深入解析Redis数据一致性问题(一)

mhr18 2024-12-04 13:36 14 浏览 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将数据库中的旧值更新到缓存之前,就已经把缓存删除了),那么这次删除就没任何意义,也解决不了一致性问题。所以,这个休眠时间设置是关键所在。

但不幸的是,这确实很难给定一个准确答案,一般设置原则是“大于缓存的读写时间即可,又或者是大于从数据库读取数据+写入缓存的时间和即可”。实际上,这个时间只又在经过不断的压测和实际环境运行,才能够找到一个合理的预估时间,从而尽可能的去降低发生数据不一致性问题的概率。

相关推荐

redis 7.4.3更新!安全修复+性能优化全解析

一、Redis是什么?为什么选择它?Redis(RemoteDictionaryServer)是一款开源的高性能内存键值数据库,支持持久化、多数据结构(如字符串、哈希、列表等),广泛应用于缓存、消...

C# 读写Redis数据库的简单例子

CSRedis是一个基于C#的Redis客户端库,它提供了与Redis服务器进行交互的功能。它是一个轻量级、高性能的库,易于使用和集成到C#应用程序中。您可以使用NuGet包管理器或使用以下命令行命令...

十年之重修Redis原理

弱小和无知并不是生存的障碍,傲慢才是。--------面试者总结Redis可能都用过,但是从来没有理解过,就像一个熟悉的陌生人,本文主要讲述了Redis基本类型的使用、数据结构、持久化、单线程模型...

高频L2行情数据Redis存储架构设计(含C++实现代码)

一、Redis核心设计原则内存高效:优化数据结构,减少内存占用低延迟访问:单次操作≤0.1ms响应时间数据完整性:完整存储所有L2字段实时订阅:支持多客户端实时数据推送持久化策略:RDB+AOF保障数...

Magic-Boot开源引擎:零代码玩转企业级开发,效率暴涨!

一、项目介绍基于magic-api搭建的快速开发平台,前端采用Vue3+naive-ui最新版本搭建,依赖较少,运行速度快。对常用组件进行封装。利用Vue3的@vue/compiler-sfc单文...

项目不行简历拉胯?3招教你从面试陪跑逆袭大厂offer!

项目不行简历拉胯?3招教你从面试陪跑逆袭大厂offer!老铁们!是不是每次面试完都感觉自己像被大厂面试官婉拒的渣男?明明刷了三个月题库,背熟八股文,结果一被问项目就支支吾吾,简历写得像大学生课程设计?...

谷歌云平台:开发者部署超120个开源包

从国外相关报道了解,Google与Bitnami合作为Google云平台增加了一个新的功能,为了方便开发人员快捷部署程序,提供了120余款开源应用程序云平台的支持。这些应用程序其中包括了WordPre...

知名互联网公司和程序员都看好的数据库是什么?

2017年数据库领域的最大趋势是什么?什么是最热的数据处理技术?学什么数据库最有前途?程序员们普遍不喜欢的数据库是什么?本文都会一一揭秘。大数据时代,数据库的选择备受关注,此前本号就曾揭秘国内知名互联...

腾讯云发布云存储MongoDB服务

近日,著名安全专家兼Shodan搜索引擎的创建者JohnMatherly发现,目前至少有35000个受影响的MongoDB数据库暴露在互联网上,它们所包含的数据暴露在网络攻击风险之中。据估计,将近6...

已跪,Java全能笔记爆火,分布式/开源框架/微服务/性能调优全有

前言程序员,立之根本还是技术,一个程序员的好坏,虽然不能完全用技术强弱来判断,但是技术水平一定是基础,技术差的程序员只能CRUD,技术不深的程序员也成不了架构师。程序员对于技术的掌握,除了从了解-熟悉...

面试官:举个你解决冲突的例子?别怂!用这个套路……

面试官:举个你解决冲突的例子?别怂!用这个套路……最近收到粉丝私信,说被问到:团队技术方案有分歧怎么办?当场大脑宕机……兄弟!这不是送命题,是展示你情商+技术判断力的王炸题!今天教你们3招,用真实案例...

面试碰到MongoDB?莫慌,跟面试官这样吹MongoDB 复制集

推荐阅读:吊打MySQL:21性能优化实践+学习导图+55面试+笔记+20高频知识点阿里一线架构师分享的技术图谱,进阶加薪全靠它十面字节跳动,依旧空手而归,我该放弃吗?文末会分享一些MongoDB的学...

SpringBoot集成扩展-访问NoSQL数据库之Redis和MongoDB!

与关系型数据库一样,SpringBoot也提供了对NoSQL数据库的集成扩展,如对Redis和MongoDB等数据库的操作。通过默认配置即可使用RedisTemplate和MongoTemplate...

Java程序员找工作总卡项目关?

Java程序员找工作总卡项目关?3招教你用真实经历写出HR抢着要的简历!各位Java老哥,最近刷招聘软件是不是手都划酸了?简历投出去石沉大海,面试邀请却总在飞别人的简历?上周有个兄弟,13年经验投了5...

Java多租户SaaS系统实现方案

嗯,用户问的是Java通过租户id实现的SaaS方案。首先,我需要理解用户的需求。SaaS,也就是软件即服务,通常是指多租户的架构,每个租户的数据需要隔离。用户可能想知道如何在Java中利用租户ID来...

取消回复欢迎 发表评论: