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

news2024/11/17 21:35:58

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

一般的业务场景都是读多写少的,当客户端的请求太多,对数据库的压力越来越大,引入缓存来降低数据库的压力是必然选择,目前业内主流的选择基本是使用 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,对于并发很高的场景,我们可以在操作数据库之前通过消息队列来降低客户端对数据库的请求压力。

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

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

相关文章

【移动开发】最新uniapp开发的多端影视APP+后端对接的苹果CMS源码(可打包多端app)

介绍: 分享一款uniapp开发的多端影视APP源码,后端对接的是苹果10CMS。 这个APP有缓存、投屏、回放记录、收藏等功能,非常完善。 会员中心也使用苹果cms的数据。 可以打包到多个端的代码。 本APP前端用的UI框架是特漂亮的colorUI&#xff0…

当我们谈外包,我们在谈什么?

大家好,我是苍何,周末发小来找我玩耍,聊到了他当初在华为外包的经历,感触挺大的,给大家做下分享。 小 Z 大学专科材料专业毕业,毕业后报名了 IT 培训班,学习了一年,之后面试去了华为…

关于安卓grovvy开发(一)bugly混淆自动上报

背景 打包,mapping,上传,这些是不是都困扰了开发很多。苦于复制粘贴?反正我是。于是,有了这个 最终效果 最终的效果,就是点击项目的打包gradle,复制mapping文件且进行bugly混淆的自动上报。 …

基于单片机超声波测距语音播放

一、系统方案 本设计采用52单片机作为主控器,HC-SR04测距,液晶1602显示,按键设置报警阀值,语音报警。 二、硬件设计 原理图如下: 三、单片机软件设计 1、首先是系统初始化 uint dist; // 保存超声波模块测量到的结果…

基于SSM的网上医院预约挂号系统

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

Linux 部分IO函数

open、close函数 标准C库IO函数的说明文档在 man 3 open Linux系统IO函数说明文档在man 2 open open()函数打开文件 && errno 例子:目录下没有a.txt那么运行的时候会报错 open创建新文件 本来没有 create.txt 运行完有了 ll可看文…

【LeetCode-中等题】209. 长度最小的子数组

文章目录 题目方法一:滑动窗口:方法二: 题目 方法一:滑动窗口: 参考图解动画:长度最小的子数组 class Solution { //方法一:滑动窗口public int minSubArrayLen(int target, int[] nums) {int n nums.l…

解决jsp/html界面跳转servlet出现404错误的方法

解决jsp/html界面跳转servlet出现404错误的方法 最近在学习黑马项目过程中遇到的问题 问题一: 检查页面的跳转路径和名称拼写是否正确 问题二: tomcat发布项目时所使用的路径名称与项目不同 在idea右上角点击如图圈住的按钮 在deployment中更改出现…

【SpringCloud微服务项目学习-mall4cloud项目(1)】——环境部署,构建与运行

环境部署,构建与运行 mall4cloud项目介绍源码地址 开发环境搭建pom搭建项目运行前端运行 mall4cloud项目介绍 mall4j商城系统 首先介绍一下mall4j,是一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离、防范xss攻击、拥有分布…

Redis的基本概念与基础用法(1)

在节假日前12306的访问量就会急剧增加,在这种海量用户高并发的情况下就容易出现网站崩溃的情况,造成网站奔溃的罪魁祸首就是关系型数据库,因为关系型数据库有: 性能瓶颈:磁盘IO性能低下扩展瓶颈:数据关系复…

【三维重建】Ubuntu18.04安装COLMAP

Ubuntu18.04安装COLMAP 文章目录 Ubuntu18.04安装COLMAP前言安装COLMAP:安装CUDA, cuDNN安装依赖项安装Ceres优化库安装glog(可选)配置并编译COLMAP运行COLMAP 总结 前言 COLMAP是一种通用的运动结构(SfM)和多视图立体(MVS)管道,具有图形和命令行界面。…

RBTree(红黑树)模拟实现(插入)

目录 红黑树的性质 红黑树的模拟插入 叔叔存在且为红色 叔叔不存在 旋转情况​​​​​​​ 叔叔存在且为黑色 总结 插入实现 节点 插入逻辑 左单旋 右单旋 红黑树是一颗平衡搜索二叉树,但是红黑树并不像 AVL 树一样是高度平衡二叉树,任意一…

正在吞食世界的Python!

谁能想到,30年后,一条蟒蛇因为人工智能而席卷了全世界!这一切,都源于1989年的那个圣诞节。 一个名叫Guido van Rossum程序员在荷兰的阿姆斯特丹呆着,无所事事的圣诞假期有点无聊。为了打发时间,他开发了一…

前端中的事件委托

前端小知识 事 件 委 托 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/132819265 【介绍】&#xff1…

MATLAB语言 实验一 MATLAB工作环境熟悉及简单命令的执行

一、 实验名称 MATLAB工作环境熟悉及简单命令的执行 二、 实验目的 熟悉MATLAB的工作环境,学会使用MATLAB进行一些简单的运算。 三、实验内容 MATLAB的启动和退出,熟悉MATLAB的桌面(Desktop),包括菜单&#xff08…

抖音视频批量智能剪辑/智能一键成片功能如何技术开发源头?

抖音seo,视频剪辑,批量发布,账号矩阵管理,无人直播自动询盘锁定客户,想实现以上功能都要有正规的接口权限,这个权限接口已经在前面文章发过。 一、剪辑技术开发 智能剪辑:咱们研发公司自主研发…

初识Vue3

目录 创建实例 Vue3生命周期 响应式基础 为什么要使用 ref? 声明响应式状态 ref()和reactive() toRef()和toRefs() 创建实例 通过对Vue2的学习,我们可以这样在Vue2中创建一个实例: var vm new Vue({// 选项 }) 或者通过Vue全局api…

webrtc-m79-测试peerconnectionserver的webclient-p2p-demo

1 背景 webrtc的代码中有peerconnectionclient和peerconnectionserver的例子,但是没有对应的web端的例子,这里简单的写了一个测试例子,具体如下: 2 具体操作 2.1 操作流程 2.2 测试效果 使用webclient与peerconnectionclient的…

Windows安装MySQL8.0完整教程

很多朋友在安装MySQL的时候,总会遇到各种各样的问题。本文来教你怎样正确安装MySQL。 一、 下载MySQL 如果已经下载好了可以忽略,我下面提供两个版本的下载链接 阿里云盘 夸克云盘 链接:https://pan.quark.cn/s/1894623c2e6a 提取码&…

Go入门教程

什么是Go语言? Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回…