高并发下Redis缓存与数据库双写一致性问题原理分析和解决方案

news2024/11/26 21:34:27

目录

    • 一、什么是缓存与数据库双写不一致性
    • 二、常见保证高并发下双写一致性方案
      • 2.1、延迟双删(不可靠)
      • 2.2、分布式读写锁(可靠)
      • 2.3、MQ异步消费(不可靠)
      • 2.4、订阅数据库变更日志(不可靠)
    • 三、总结
      • 3.1、如何选择一个合适的方案
        • 3.1.1、先更新数据库再删除缓存(普通一致性)
        • 3.1.2、延时双删、MQ异步消费、订阅数据库变更日志(高级一致性)
        • 3.1.3、分布式读写锁(终极一致性)
      • 3.2、其它方案
        • 3.2.1、商品信息更新后手动刷新缓存
        • 3.2.2、通过延时消息实现异步删除缓存

一、什么是缓存与数据库双写不一致性

      当数据发生更新时,我们不仅要更新数据库数据,还要更新缓存。如果缓存和数据库都更新的话,就会存在以下两个问题,是先更新数据库还是先更新缓存,在单线程执行没有异常的情况下无论是谁先谁后,缓存和数据库都可以保持一致性,但是异常的情况或者并发操作下都有可能会出现双写不一致性的情况,这里会重点说明高并发下如何保证Redis缓存与数据库双写一致性。

  • 单线程双写不一致示例
    对于这种情况其实很好解决,因为是在一个数据库事务中别做缓存修改,直接做缓存删除就行了,数据库更新后将对应缓存删除即可,在查询操作中在进行缓存,就算缓存删除失败或者删除成功后其它业务出现异常也不影响。
    在这里插入图片描述

  • 高并发双写不一致示例
    一般情况下我们都是先更新数据库然后删除缓存,在查询时如果查询到数据则将数据写入缓存。在并发操作时这里可以看到,在T2线程查询数据库写入缓存时可能会存在查询到数据准备写入缓存时,T3线程正好在这期间将新数据写入数据库并且删除缓存,那么这里T2线程写入缓存的数据就属于脏数据。
    在这里插入图片描述

二、常见保证高并发下双写一致性方案

      对于并发几率很小的数据(如单个用户维度的订单数据、用户数据等),这种几乎不用考虑这个问题,很少会发生缓存不一致,可以给缓存数据加上过期时间,每隔一段时间触发读的主动更新即可,就算并发很高,如果业务上能容忍短时间的缓存数据不一致(如商品名称,商品图片等),缓存加上过期时间依然可以解决大部分业务对于缓存的要求。
      在代码实现中一般情况下我们都是先更新数据库然后删除缓存,只有确保数据库数据更新成功才能去动缓存,这应该是大部分人所使用的方式,这里也会围绕这个流程做说明,有几个常见可行的方法延迟双删、分布式锁、MQ异步消费、订阅数据库变更日志…,像什么先删除缓存在更新数据库这类型的方法是没有意义的还是会有问题,这里针对这几个方法做说明。

2.1、延迟双删(不可靠)

      延迟双删理论上也是不可靠的,只是能在一些特定情况下降低出现双写不一致性的情况,比如3个线程同时进入T2线程先查询数据库拿到结果准备写缓存时,T1线程拿到CPU执行权开始执行修改数据库逻辑,修改好后延时0.5S在删除缓存,在这期间T2线程执行写入缓存操作这个写入数据就是脏数据,0.5S后T1线程将缓存删除,先忽略T3线程这样T4线程在进行查询时缓存中就是没有值的会重新查询数据库获取最新值,不过这个逻辑很脆弱,T3线程可能因为一些情况导致一直没有执行,在T4线程查询数据库后突然就开始执行了,这个时候T3线程执行了修改逻辑和删除缓存逻辑T4线程才把之前查询的数据写入缓存那么这个数据就是脏数据,又出现缓存不一致问题。

在这里插入图片描述

2.2、分布式读写锁(可靠)

      如果不能容忍缓存数据不一致,可以通过加读写锁保证并发读写,读读共享、读写互斥、写写互斥,这样可以保证业务的双写一致,但是这样做Redis性能开销会比较大每个查询请求都要调用Redis进行锁判断,而且要控制好锁避免出现等待时间过长和死锁问题,这里提供两个业务实现流程各有优缺点可以根据具体业务选择。

  • 流程一:获取锁失败,轮询等待获取锁成功。

在这里插入图片描述

  • 流程二:获取锁失败,执行查询流程但是不将数据写入缓存
    在这里插入图片描述

2.3、MQ异步消费(不可靠)

      通过MQ异步消费和延时双删其实是类似的都是只能解决一部分问题,例如我这里举例的,3个线程都是并行的,T1线程修改数据库修改成功后发送MQ消息,T2线程查询商品信息写入缓存写入的是脏数据,先不看T3线程,在T2线程写入缓存后MQ消费到消息将缓存给删除了,那么后面再有查询过来就能获取到最新的数据,现在看看T3线程,在查询商品信息准备写入缓存这时MQ消费到消息先删除缓存,然后T3线程在写入缓存,这个时候缓存中又是脏数据了,和延时双删差不多。

在这里插入图片描述

2.4、订阅数据库变更日志(不可靠)

      在MySQL中修改一条数据,MySQL 就会产生一条变更日志(Bin Log),我们可以订阅这个日志,获取到具体的操作数据,然后再根据这条日志数据,去删除对应的缓存,订阅变更日志比较比较成熟的开源中间件,比如阿里的 canal,通过canal订阅到Bin Log然后将数据发送给MQ交给对应消费者处理,canal只做数据采集不做业务处理,这种方式又和MQ异步消费类似了,无非就是将数据变更通过canal来采集然后发送给MQ还是会存在MQ异步消费一样的问题。

在这里插入图片描述

三、总结

      这里介绍了一些方案,除了加锁其它方案都不能完全保证双写一致性问题,其实还有别的方案也能保证一致性问题,要结合业务设计这里不深入,以上我们针对的都是读多写少的情况加入缓存提高性能,如果写多读多的情况又不能容忍缓存数据不一致,那就没必要加缓存了,可以直接操作数据库。放入缓存的数据应该是对实时性、一致性要求不是很高的数据。切记不要为了用缓存,同时又要保证绝对的一致性做大量的过度设计和控制,增加系统复杂性。

3.1、如何选择一个合适的方案

      不同的缓存策略对于不同的应用场景和需求可能会产生不同的影响,因此需要根据具体情况选择合适的缓存策略和组合方式。
在这里插入图片描述

3.1.1、先更新数据库再删除缓存(普通一致性)

      一般情况下我们写代码肯定都是先更新数据库才会去删除缓存的,后面两个方案也基于先更新数据库才会去删除缓存,当然不同业务可能不同,我们这里的商品信息肯定是先更新数据库才会去删除缓存,这样是能保证最低限度的一致性问题的,适合并发不高读多写少的业务,并且实现简单不依赖其它中间件。

3.1.2、延时双删、MQ异步消费、订阅数据库变更日志(高级一致性)

      延时双删、MQ异步消费、订阅数据库变更日志这三个方案的目的其实都是为了提升一致性的措施,在有条件的情况下是可以使用的,如果考虑极端情况其实还是还存在问题,并且实现起来会稍微麻烦一些,MQ异步消费、订阅数据库变更日志还需要依赖外部中间件增加系统复杂度导致出现一些不可预期问题。

3.1.3、分布式读写锁(终极一致性)

      使用分布式读写锁是能完全保证一致性的,如果业务数据一致性要求非常高那可以考虑使用分布式读写锁,如果一致性要求并没有很高其实使用锁来解决并不是一个很合适的方式,比如商品信息只做展示使用,并且只有后台编辑时才会改变,那么查询时也进行锁判断其实增加了很多不必要的开销,每一次查询请求都要调用Redis进行加锁,在使用分布式读写锁解决一致性问题时一定要做好压力测试,避免线上资源不足。

3.2、其它方案

      所有方案都需要结合业务,不同业务的最优方案都不同,这里列举一些实际案例的解决方案。

3.2.1、商品信息更新后手动刷新缓存

      无论那种方案,其实手动刷新还是挺靠谱的,后台管理设计一个批量刷新商品信息缓存功能,当商品信息变更后如果担心缓存没有更新成最新商品信息或者实际看到数据就是老数据,完全可以使用手动刷新来解决这个问题,选择需要刷新缓存的商品批量刷新即可。

3.2.2、通过延时消息实现异步删除缓存

      其实这个设计原理就是结合了延时双删、MQ异步消费、订阅数据库变更日志,在我们商品信息更新后将缓存删除,并且在投递一个延时消息或者订阅数据库变更日志投递一个延时消息,假设延时3s执行,消费者3s后收到这个消息将缓存再删除一次,这期间就算将老数据插入了缓存中,在这一次3s延时消费中也能将这个缓存删除,如何还要考虑极限情况那就延时时间在长一点5s 10s 15s …,因为是MQ异步消费不会影响主业务并且商品信息修改频次不会很高,而且只是刷新缓存操作也很快,这个方案是完全可行的,只是延时时间要根据业务和实际情况把关好。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年十大地推拉新接单平台和网推接单平台,都是一手单

2023年做拉新推广的地推人员,一定不要错过这十个接单平台,助你轻松找到一手单,这10个平台分别是: 主推:“聚量推客” 一手官签接单平台 一手官方邀请码 000000 1. 聚量推客: “聚量推客”汇聚了众多市场…

怎样选择适合自己的ITSM软件?

市场上ITSM解决方案琳琅满目,每种解决方案都有其优点和缺点。这使选择决策过程变得复杂,当组织机构决定投资ITSM软件时,很难如愿选择到一款最适合自己的帮助台软件。 而小编有一套系统的决策方法,可以帮助您简化评估过程&#xff…

Dart(一):Dart入门

Dart入门 Dart安装创建项目安装依赖(以http为例)依赖库查询地址添加依赖编写运行示例 dart常用命令引用核心库、自定义库、第三方库数据类型Numbers (int, double)Strings (String)Booleans (bool)Lists (List)Maps (Map)Sets (Set)Null (null)Records (…

Django初窥门径-自定义附件存储模型

前言 Django自带了一个名为FileField的字段,用于处理文件上传。然而,有时我们需要更多的控制权,例如定义文件的存储路径、文件名以及文件类型。在本篇文章中,我们将探讨如何自定义Django附件存储模型。 创建attachment应用 pyt…

Unity3d C#实现编辑器不运行状态下执行的脚本

第一章方式: 函数前面 [ContextMenu("Play")] ,Inspector面板右键调用 第二种方式: OnValidate() ,值改变自动执行 using UnityEngine; using System.Linq;public class NightController : MonoBehaviour {pub…

只需十分钟,快速入门Python3!

文章目录 前言1. 原始数据类型和运算符2. 变量和集合3. 流程控制和迭代器4. 函数5. 类6. 模块7. 高级用法关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小…

软考的好处 拥有软考证书的8大作用

文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 1、软考证书认可度高 近年来《国家职业资格…

YOLOv5改进之CODConv,全维动态卷积再改进

目录 一、理论部分 网络结构 二、CODConv 代码 应用到YOLOv5中 一、理论部

本地生活餐饮视频怎么拍摄能有更多流量?如何批量生产呢?

本地生活近几年特别的火,所以到现在各类内容雷同性也比较高,视频缺少新的创意和玩法,像餐饮店的视频,大部分都是拍顾客进门、拍餐饮店座无虚席的实景……作为用户,其实早就已经看腻了。 今天推荐本地生活餐饮店商家拍…

旅游管理与推荐系统Python+Django网页平台+协同过滤推荐算法【计算机毕设项目】

一、介绍 旅游管理与推荐系统。本系统使用Python作为主要编程语言,前端采用HTML、CSS、BootStrap等技术实现界面展示平台的开发,后端使用Django框架处理用户响应请求,并使用Ajax等技术实现前后端的数据通信。本系统主要功能有: …

【ROS】Nav2源码之nav2_collision_monitor详解

【ROS】郭老二博文之:ROS目录 1、简介 nav2_collision_monitor碰撞监视器是一个节点,为机器人提供了一个额外的安全级别。 它使用来自传感器的数据,执行若干与避免碰撞相关的任务,绕过成本图和轨迹规划器,监测是否需要紧急停止或者减速。 2、安全行为 Collision Mon…

ARMday03(寄存器读写、栈、程序状态寄存器、软中断和异常、混合编程)

单寄存器内存读写指令 将一个寄存器中的数值写入到内存,或者从内存中读取数据放在某一个指定寄存器中 指令码和功能 1.向内存中写: str{条件码} 目标寄存器,[目标地址]:将目标寄存器的4字节数值写入到目标地址为首地址的空间中 strh{条件码…

0X03

红包题第二弹 看到源码里面的提示 ?cmdphpinfo(); 看到源码 kk 关键点就是有两个正则表达式 第一个 preg_match("/[A-Za-oq-z0-9$]/",$cmd) 第二个 preg_match("/\~|\!|\|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\|\&q…

【MySQL日志与备份篇】其他数据库日志

其他数据库日志 文章目录 其他数据库日志1. 概述1.1 日志类型1.2 日志的弊端 2. 慢查询日志(slow query log)3. 通用查询日志(general query log)4. 错误日志(error log)5. 二进制日志(bin log)5.1 参数5.2 查看日志5.3 格式5.3.1 STATEMENT模式 (基于SQL语句的复制…

Web组件

开发者使用Vue、React等框架来使用及创建定制的组件,Web组件是浏览器原生支持的替代这些框架的特性,主要涉及相对比较新的三个Web标准。这些Web标准允许JS使用新标签扩展HTML,扩展后的标签就是自成一体的、可重用的UI组件。 1 HTML模版 Docu…

数据结构大体体系

逻辑结构 线性结构线性表一串珠子用线连起来,这就是典型的“线性存储结构”。每颗珠子之间的关系结构也很简单,包括头尾的话,它们最少有一个关系对象,而中间的珠子无论前后都只有一个关系对象,即 one-to-one栈队列字符…

webgoat-Sensitive Data Exposure 敏感信息泄露

insecure login不安全的登录 Encryption is a very important tool for secure communication 0x02 点击login,可以看到payload里的username和password,输入后点击submit即可。 这题的目的是说明,信息传输过程中需要加密,如不…

【C语言 | 符号】C语言中符号易出错的地方

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…

一次简单的测试

文章现写的,部分内容有些着急 0x01 测试过程 开局登录框,没有测试SQL注入与XSS(菜鸟,测也测不出来。) 有system用户,尝试弱口令,无果! 直接namp来一下子端口探测吧 发现有3306与8888 3306 mysql测试 88…

python 之异常处理结构

文章目录 常见的异常处理表现形式1. SyntaxError2. NameError3. TypeError4. IndexError5. KeyError6. ZeroDivisionError7. FileNotFoundErrortry……except …… 结构1. try 块2. except 块示例:多个except块try……except ……else 结构结构说明:示例…