当前位置: 首页 > news >正文

Redis 数据一致性方案的分析与研究

点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~

一般的业务场景都是读多写少的,当客户端的请求太多,对数据库的压力越来越大,引入缓存来降低数据库的压力是必然选择,目前业内主流的选择基本是使用 Redis 作为数据库的缓存。但是引入缓存以后,对我们系统的设计带来了很大的挑战,其中缓存和数据库的数据一致性问题就是一个非常棘手的问题,今天我们就来聊一聊在项目中,我们用什么方案来解决数据一致性的问题。

1

Redis 的使用场景

bbceb65e690e776cda4de5b52d6d0f31.png

我们在实际项目中,通常情况下的应用是读多写少的,我们一般用 Redis 来解决读数据库的压力问题,也就是说用 Redis 作为缓存来减轻由于客户端频繁查询数据库而对数据库造成的压力。

因此在项目中由客户端直接查询数据库返回数据,变成了客户端查询 Redis 缓存,缓存有数据则直接返回数据,缓存没有数据再去查询数据库,同时数据库的数据写入到 Redis 缓存。

e455a1d6f394b4ca67416239a94db6dc.png

在以上的使用场景中,我们一般缓存热点数据,比如电商平台中的热销商品,用户的登录信息,新闻类平台中的热点新闻等。

2

数据一致性问题产生的原因

be3866656da2c9c3d728e190c26f3f5e.png

当数据库的数据发生变化的时候,引入 Redis 缓存以后,增加了数据操作难度,既要操作数据库又要操作 Redis,对  Redis和数据库的操作有 2 种方案:

1、先操作 Redis,再操作数据库

2、先操作数据库,再操作 Redis

我们无论选择以上的哪个方案,都希望数据操作要么都成功,要么都失败,我们不希望看到一个失败,一个成功的结果,因为这样就产生了数据不一致的问题。

举个例子说明:

假设 Redis 里缓存了一个热点商品数据,有个 key 为 1001 的商品名称为“华为手机”,数据库里这个 1001 号的商品名称也是华为手机;此时商家觉得商品名称叫华为手机有点宽泛,需要精确一下,把 1001 号商品的名称修改为其对应的具体名称“华为 P40 Pro”。

1、如果我们选择先操作 Redis,再操作数据库的方案,当操作 Redis 成功,操作数据库失败的时候,Redis 里的名称修改为“华为 P40 Pro”,但是数据库的名称还是“华为手机”,产生了数据不一致问题。

2、如果我们选择先操作数据库,再操作 Redis 的方案,当操作数据库成功,操作 Redis 失败的时候,数据库里的名称修改为“华为 P40 Pro”,但是 Redis 的名称还是“华为手机”,产生了数据不一致问题。

由于 Redis 和数据库是 2 个不同的中间件,我们无法通过事务来很好地解决数据一致性的问题,因此只能在数据实时一致性和系统性能上做权衡,选择一个可以接收的方案。

因为数据库是稳定的持久化的系统,比 Redis可靠,我们一般都是以数据库的数据为准,解决这个数据一致性问题的原则就是:我们可以为 Redis 缓存数据设置一个过期的时间,当 Redis 数据过期了就去数据库查询,然后再把数据库的数据写入 Redis 缓存中,确保数据的一致性。

如果在这个过期时间范围内,数据发生了更新操作,当更新操作有一个失败,有一个成功,就会产生不一致的问题,这个过期时间设置的太短了,数据库的压力还是很大,过期时间设置的太长了,不一致性的问题就会凸显,因此我们需要基于以数据库的数据为准的原则下,继续探讨数据一致性问题的解决方案。

3

数据更新时,如何操作 Redis

60417e1c8e4db6dba7bd27a45f422138.png

当数据更新时,操作数据库我们都很熟悉,直接更新数据库的值就可以了,但是操作 Redis,我们应该怎么办?

1、有人会说,这还不简单,数据变化时,直接更新缓存

2、也有人会说数据变化时把缓存的数据删掉,当查询请求发现缓存没有数据,就会从数据库加载新的数据。

以上的两种方法都可以,那我们该如何选择呢?

如果我们选择更新 Redis 的方案,我们要评估一下更新的代价大不大,比如拿到数据更新到 Redis 需要经过很多表的关联查询,或者多个接口的调用查询,经过大量计算才能得到数据的话,就不要使用更新 Redis 的方案了,因为面对这么复杂的更新流程,不如直接删掉方便。如果要更新的数据直接可以获取到,那么选择更新 Redis 的方案就是可行的。

直接删除 Redis 缓存数据是更加简单的方案,因为缓存数据删了,只能从数据库读取了,这样可以避免不一致性,互联网公司一般选择删除 Redis 的方案。

下面我们用一张图来表示可选的方案:

33dad3e37ef2cb586358a196ac9e2f35.png

通过上图,我们可以看出当数据发生变化时,Redis 与数据库的处理流程我们可以从 Redis更新还是删除这 2 个方面分析,然后再从先操作 Redis还是先操作数据库这 2 个方面分析,下面我们按此思路继续深入分析。

4

选择 Redis 更新方案

c22ccceea50c3deec728e9147925e2f7.png

1、先更新 Redis,再更新数据库

首先不推荐选择这种方案。

因为我们的数据一致性的基本原则是以数据库的数据为准,所以先更新缓存的话,会存在缓存更新成功,数据库更新失败的情况,此时面临的问题就需要回滚掉刚才更新缓存成功的操作,那么就需要从业务代码里加入很多判断逻辑来处理这种异常,需要考虑数据变化是 insert,update,delete 等,根据不同的场景执行不同的回滚方案,这种回滚操作比较麻烦,对我们业务代码的倾入性也比较大,所以不推荐选择这种方案。

当然,如果业务层面可以接受这种数据的不一致性,异常情况就不需要考虑,选择这种方案也是可以的。

2、先更新数据库,再更新 Redis

先更新数据库,再更新 Redis 的方案,由于把数据库操作放到了前面,如果数据库操作失败,那么客户端再发起一次就可以了。

如果数据库操作成功,Redis 操作失败,此时由于数据库的数据已经设置成功,我们的重点就是讨论如何把 Redis的数据操作成功即可。从客户端层面来看,他提交的数据已经在我们系统里存在了,此时的问题就是我们系统内部如何解决的问题。

对于 Redis 操作失败,我们可选的方案有以下几个,根据业务要求的数据一致性级别,进行权衡选择即可。

1、不做任何操作,等着Redis里的缓存数据过期后,自动从数据库同步最新的数据,此时最严重的数据不一致性周期就是在缓存过期的一段时间(考虑一下这个过期时间的范围);如果在这个时间段内,又有新的更新请求,也许这次就更新缓存成功了。

2、如果数据一致性要求比较高,那么 Redis 操作失败后,我们把这个操作记录下来,异步处理,用 Redis 的数据去和数据库比对,如果不一致,再次更新缓存确保缓存数据与数据库数据一致。

5

选择 Redis 删除方案

32e69376e2e67251301a76591863cec7.png

由于目前数据发生变化,主流的方案是选择删除 Redis,所以我们对这个方案重点进行分析。

1、先更新数据库,再删除 Redis

我们如果更新数据库成功,删除 Redis 失败,那么 Redis 里存放的就是一个旧值,也就是删除缓存失败导致缓存和数据库的数据不一致了。

对于删除 Redis 失败的异常情况来进行分析,我们一般有如下的方案:

1、我们可以重试删除,比如:我们可以把删除动作发送到消息队列 MQ,MQ的消费者再去删除这个key,一致尝试删除操作,确保缓存删除成功,这个方案缺陷就是删除缓存的地方,通过代码实现对业务逻辑产生了入侵。

2、另外一种方案,就是完全异步的方案,对业务逻辑没有入侵,通过监听 binlog 的变化来删除缓存。我们更新数据库会产生 binlog,我们可以用一个服务监听数据库 binlog 的变化,异步去删除缓存,阿里开源的 canal 就是可以监听 binlog 的工具,只要数据发生变化就可以删除 Redis 的数据。

2、先删除 Redis,再更新数据库

我们如果删除 Redis 缓存成功,更新数据库失败的话,因为我们是以数据库为准,再查一次就可以了,这个方案看似很理想,感觉没什么问题,其实在并发下也可能产生数据一致性问题。

我们先看一个并发环境下的更新和查询流程:

7027b359d045caa00ec07b8a0831836b.png

上面的图表示,Thread-1 是个更新流程,Thread-2 是个查询流程,cpu 执行顺序是:Thread-1 删除缓存成功,此时 Thread-2 获取到 CPU 执行查询缓存没有数据,然后查询数据库把数据库的值写入缓存,因为此时 Thread-1 更新数据库还没有执行,所以缓存里的值是一个旧值(old),最后 CPU 执行 Thread-1 更新数据库成功的代码,那么此时数据库的值是新增(new),这样就产生了数据不一致行的问题。

要解决以上的问题,我们我们一般有如下的可选方案:

1、并发下多线程操作能不能让同一个操作里的 2 个步骤同时操作完成,下一个线程才能执行,让各个线程排队操作,有人说加锁,如果一个服务部署到了多个机器,就变成了分布式锁,或者是分布式队列按顺序去操作数据库或者 Redis,带来的副作用就是数据库本来是并发的,现在变成串行的了,所以这个方案看起来不太可行,加锁或者排队执行的方案降低了系统性能。

2、另外一种放啊就是延时双删的方案,也就是先删除缓存,再更新数据库,当更新数据后休眠一段时间再删除一次缓存。

伪代码如下:

redes.del(key);
db.update(data);
Thread.sleep(2000);
redes.del(key);

以上谈到的都是一些解决方案,没有说哪个方案更好,或者哪个方案不行,以上的方案都是基于实时一致性和系统性能的情况下进行权衡选择,根据不同的业务,不同的数据一致性要求,结合系统的性能综合考虑,选择适合自己系统的方案就好。

总的来说,由于我们的基本原则是以数据库为准,那么我们选择的方案就应该把操作数据库放到前面,也就是说我们应该先操作数据库,再操作 Redis,对于并发很高的场景,我们可以在操作数据库之前通过消息队列来降低客户端对数据库的请求压力。

相关文章:

Redis 数据一致性方案的分析与研究

点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~ 一般的业务场景都是读多写少的,当客户端的请求太多,对数据库的压力越来越大,引入缓存来降低数据库的压力是必然选择,目前业内…...

【网络安全】黑客自学笔记

1️⃣前言 🚀作为一个合格的网络安全工程师,应该做到攻守兼备,毕竟知己知彼,才能百战百胜。 计算机各领域的知识水平决定你渗透水平的上限🚀 【1】比如:你编程水平高,那你在代码审计的时候就会比…...

深入解析Perlin Simplex噪声函数:在C++中构建现代、高效、免费的3D图形背景

引言 在计算机图形中,噪声是一个经常被讨论的话题。无论是为了制造自然的纹理,还是为了模拟复杂的现实世界现象,噪声函数都在其中起着关键作用。而在众多噪声函数中,Perlin Simplex 噪声无疑是最受欢迎的一种。其原因不仅在于其干…...

【计算机辅助蛋白质结构分析、分子对接、片段药物设计技术与应用】

第一天 上午 生物分子互作基础 1.生物分子相互作用研究方法 1.1蛋白-小分子、蛋白-蛋白相互作用原理 1.2 分子对接研究生物分子相互作用 1.3 蛋白蛋白对接研究分子相互作用 蛋白数据库 1. PDB 数据库介绍 1.1 PDB蛋白数据库功能 1.2 PDB蛋白数据可获取资源 1.3 PDB蛋白数据库对…...

免费开箱即用微鳄售后工单管理系统

编者按:本文介绍基于天翎MyApps低代码平台开发的微鳄售后工单管理系统, 引入低代码平台可以帮助企业快速搭建和部署售后工单管理系统, 以工作流作为支撑,在线完成各环节数据审批,解决售后 工单 服务的全生命周期过程管…...

vant 组件库的基本使用

文章目录 vant组件库1、什么是组件库2、vant组件 全部导入 和 按需导入的区别3、全部导入的使用步骤:4、按需导入的使用步骤:5、封装vant文件包 vant组件库 该项目将使用到vant-ui组件库,这里的目标就是认识他,铺垫知识 1、什么…...

HTML常用基本元素总结

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title> biao qian</title> </head> <body><h1>这是标题1</h1> <h2>这是标题2</h2> <h3>这是标题3</h3><p> 这…...

msvcp140.dll重新安装的解决方法是什么?(最新方法)

msvcp140.dll 是 Microsoft Visual C Redistributable 的一个动态链接库文件&#xff0c;它包含了 C 运行时库的一些函数和类&#xff0c;对于许多应用程序和游戏来说都是必需的。如果您的系统中缺失了这个文件&#xff0c;可能会导致程序无法正常运行。下面我们将分享修复 msv…...

USI-0002 SDI-1624 HONEYWELL ,用于工业和物流4.0的人工智能

USI-0002 SDI-1624 HONEYWELL &#xff0c;用于工业和物流4.0的人工智能 生产、仓库、运输——生产、储存、分拣或包装货物的地方&#xff0c;也是提货的地方。这意味着几个单独的货物从存储单元如箱子或纸盒中取出并重新组装。有了FLAIROP(机器人采摘的联邦学习)项目费斯托…...

计算机竞赛 深度学习 python opencv 火焰检测识别

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…...

Intellij idea 2023 年下载、安装教程、亲测可用

文章目录 1 下载与安装IDEA2 常用设置设置 Java JDK 版本自动导入包、移除包IDEA 自动生成 author 注释签名java.io.File 类无法自动提示导入&#xff1f;高亮显示与选中字符串相同的内容IDEA 配置 MavenIDEA 连接 Mysql 数据库 3 参考文章 1 下载与安装IDEA 首先先到官网下载…...

AI文本创作在百度App发文的实践

作者 | 内容生态端团队 导读 大语言模型&#xff08;LLM&#xff09;指包含数百亿&#xff08;或更多&#xff09;参数的语言模型&#xff0c;这些模型通常在大规模数据集上进行训练&#xff0c;以提高其性能和泛化能力。在内容创作工具接入文心一言AI能力后&#xff0c;可以为…...

Kafka 集群与可靠性

文章目录 Kafka集群的目标Kafka集群规模如何预估Kafka集群搭建实战Kafka集群原理成员关系与控制器集群工作机制replication-factor参数auto.leader.rebalance.enable参数 集群消息生产可靠的生产者ISR&#xff08;In-sync Replicas&#xff09;使用ISR方案的原因ISR相关配置说明…...

【刷题】蓝桥杯

蓝桥杯2023年第十四届省赛真题-平方差 - C语言网 (dotcpp.com) 初步想法&#xff0c;x y2 − z2&#xff08;yz)(y-z) 即xa*b&#xff0c;ayz&#xff0c;by-z 2yab 即ab是2的倍数就好了。 即x存在两个因数之和为偶数就能满足条件。 但时间是&#xff08;r-l&#xff09;*x&am…...

C++入门及简单例子_4

1. 类和对象&#xff1a; #include <iostream> // 包含输入输出流库的头文件class Rectangle { // 定义名为Rectangle的类 private: // 私有成员变量部分double length; // 长度double width; // 宽度public: // 公有成员函数部分Rectangle(double len, double w…...

成集云 | 用友U8集成聚水潭ERP(用友U8主管库存)| 解决方案

源系统成集云目标系统 方案介绍 用友U8是一套企业级的解决方案&#xff0c;可满足不同的制造、商务模式下&#xff0c;不同运营模式下的企业经营管理。它全面集成了财务、生产制造及供应链的成熟应用&#xff0c;并延伸客户管理至客户关系管理&#xff08;CRM&#xff09;&am…...

提升网站效率与SEO优化:ZBlog插件集成解决方案

在创建和管理网站的过程中&#xff0c;使用合适的工具和插件可以大幅提升效率&#xff0c;并改善搜索引擎优化&#xff08;SEO&#xff09;结果。ZBlog插件是为ZBlogCMS设计的一组工具&#xff0c;它们帮助网站管理员轻松地满足各种需求&#xff0c;从采集内容到发布、推送和SE…...

C语言的编译过程详解

当我们编译C程序时会发生什么&#xff1f;编译过程中的组件有哪些&#xff0c;编译执行过程是什么样的? 什么是编译 C语言的编译过程就是把我们可以理解的高级语言代码转换为计算机可以理解的机器代码的过程&#xff0c;其实就是一个翻译的过程。 …...

无人机航测没信号?北斗卫星来解决

无人机航测是利用无人机进行地理信息的采集和处理的航测方式。相比传统的航测手段&#xff0c;无人机航测具备更高的灵活性、更低的成本和更广阔的适应性。无人机航测可以应用于土地测绘、农业植保、城市规划、自然资源调查等多个领域&#xff0c;极大地提高了测绘的效率和准确…...

Vue 03 数据绑定

Vue中有2种数据绑定的方式&#xff1a; 1.单向绑定(v-bind)&#xff1a; 数据只能从data流向页面。 2.双向绑定(v-model)&#xff1a; 数据不仅能从data流向页面&#xff0c;还可以从页面流向data。 备注&#xff1a; 1.双向绑定一般都应用在表单类元素上&#xff08;如&am…...

Symfony Doctrine Bridge 编译器传递深度解析:RegisterMappingsPass 与 RegisterUidTypePass 源码解读

Symfony Doctrine Bridge 编译器传递深度解析&#xff1a;RegisterMappingsPass 与 RegisterUidTypePass 源码解读 【免费下载链接】doctrine-bridge Provides integration for Doctrine with various Symfony components 项目地址: https://gitcode.com/gh_mirrors/do/doctr…...

NaViL-9B效果实录:复杂场景下中英文混合文字识别准确率达98.2%

NaViL-9B效果实录&#xff1a;复杂场景下中英文混合文字识别准确率达98.2% 1. 模型介绍 NaViL-9B是一款原生多模态大语言模型&#xff0c;由专业研究机构开发。它能够同时处理纯文本问答和图片理解任务&#xff0c;特别擅长复杂场景下的文字识别。在实际测试中&#xff0c;该…...

Qwen2.5-7B离线推理实战:无需GPU,CPU版本快速部署指南

Qwen2.5-7B离线推理实战&#xff1a;无需GPU&#xff0c;CPU版本快速部署指南 1. 引言 在当今AI技术快速发展的背景下&#xff0c;大型语言模型的应用越来越广泛。然而&#xff0c;许多开发者和企业在实际部署时面临GPU资源不足或成本过高的问题。本文将介绍如何在普通CPU环境…...

Llama-3.2V-11B-cot企业级落地:保险定损图片自动归因与责任链推理

Llama-3.2V-11B-cot企业级落地&#xff1a;保险定损图片自动归因与责任链推理 想象一下&#xff0c;你是一名保险公司的定损员。每天&#xff0c;你的邮箱里塞满了上百张事故现场照片——撞瘪的车头、破碎的挡风玻璃、划痕累累的车门。你需要从这些照片里&#xff0c;像侦探一…...

《一文学会管理:提示工程架构师提示内容更新的核心技巧》

一文学会管理&#xff1a;提示工程架构师提示内容更新的核心技巧 摘要 当你花费数周打磨的AI提示上线后&#xff0c;是否遇到过这样的场景&#xff1a; 用户反馈“AI回答越来越不准确”&#xff1b;业务方要求“增加新的功能模块”&#xff1b;模型升级后&#xff0c;原来的提示…...

FLUX.1-dev-fp8-dit文生图开发:C++高性能推理加速实践

FLUX.1-dev-fp8-dit文生图开发&#xff1a;C高性能推理加速实践 文生图模型的推理速度一直是开发者关注的焦点&#xff0c;特别是在需要批量生成或实时应用的场景中。今天我们来聊聊如何用C对FLUX.1-dev-fp8-dit模型进行深度优化&#xff0c;实现40%以上的推理加速。 1. 为什么…...

告别DAC!用Arduino的PWM信号和双光耦,轻松驱动LM317实现4-20mA隔离输出

用Arduino PWM与双光耦打造高性价比4-20mA隔离输出方案 在工业自动化与物联网设备开发中&#xff0c;4-20mA电流环传输因其抗干扰能力强、传输距离远等优势&#xff0c;成为模拟信号传输的黄金标准。传统方案通常依赖昂贵的DAC芯片实现数字到模拟的转换&#xff0c;而本文将揭…...

语音降噪效果主观评价:设计盲听测试与收集用户反馈

语音降噪效果主观评价&#xff1a;设计盲听测试与收集用户反馈 我们常说一个语音降噪算法好不好&#xff0c;看技术指标是一方面&#xff0c;但最终还得耳朵说了算。毕竟&#xff0c;声音是给人听的&#xff0c;处理后的音频听起来舒不舒服、清不清晰&#xff0c;这才是最直接…...

Qwen3-Embedding-4B开箱即用:SGlang部署避坑指南

Qwen3-Embedding-4B开箱即用&#xff1a;SGlang部署避坑指南 1. Qwen3-Embedding-4B简介 Qwen3-Embedding-4B是Qwen系列最新推出的文本嵌入模型&#xff0c;专为语义检索、文本分类等任务优化。作为4B参数量的中型模型&#xff0c;它在效果与效率之间取得了良好平衡&#xff…...

硬件工程师眼中的“省心”麦克风:MP421A-AT01E如何解决射频干扰与声音漂移

从“喂&#xff0c;听得到吗&#xff1f;”到“你说&#xff0c;我听着”&#xff1a;MP421A-AT01E如何让蓝牙耳机回归通话本质你有没有这样的经历&#xff1f;戴上刚买的蓝牙耳机&#xff0c;兴冲冲地给朋友打电话&#xff0c;结果对方第一句就是&#xff1a;“你那边好吵&…...