如何保证Redis的缓存和数据库中的数据的一致性?

Redis的缓存如何和数据库中的数据保持一致性?

我们都知道,Redis是一个基于内存的键值存储系统,数据完全存放在内存中,这使得它的读写速度远超传统的硬盘存储数据库。对于高访问频率、低修改率的数据,通过将它们缓存在Redis中,应用可以快速地从内存中获取数据,能够大量减少数据库的查询次数,特别是在高并发场景下,有效避免了数据库可能成为整个系统的瓶颈问题。

🍭 但是在实际开发情景中,我们如何才能保证Redis缓存中的数据和数据库中数据的一致性呢?

什么情况会出现数据的不一致?

MySQL和Redis的操作包括读操作和写操作两种,那么有哪些情况是会导致二者数据不一致的?我们先从读和写的操作流程看起~

使用Redis读取数据的场景

在这里插入图片描述
🔆使用Redis读取数据的场景

  • 客户端发起一个查询数据的接口;
  • 向Redis中查询是否存在该条数据
    • 如果存在则直接返回;
    • 不存在则向数据库中查询,再将数据库中的数据保存在Redis中,然后设置一个过期时间,之后再返回客户端。

💡 设置过期时间的主要作用就是为了保证一些冷数据过期自动淘汰,不至于一直存在Redis中占用空间

使用Redis写数据的场景

🔆对数据进行修改是分为两个步骤:

  • 修改数据库数据
  • 修改/删除Redis缓存

为什么不推荐更新缓存而是直接删除?

删除逻辑非常简单,只需要在下一个线程的时候查询Redis,查询不到再从数据库中加载即可,其副作用只是增加了一次chache miss;而更新缓存的成本更高,因为我们写入数据库的值,很多情况下并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么每次写入数据库后,都要再次计算写入缓存的值,无疑是浪费性能,显然直接删除缓存更为合适,推荐直接删除。

🍭那么接下来的问题就是先操作缓存还是先操作数据库了

先操作缓存

在这里插入图片描述

🔆先操作缓存即先将缓存清空,再对数据库数据进行修改

  • 线程一进行删除Redis缓存和修改数据库数据操作,线程二进行查询数据操作
  • 线程一将Redis缓存清空之后发生了网络延迟,此时线程二进行缓存读取
  • 线程二在Redis缓存中查询不到数据,向数据库中查询数据并将查询到的数据加载到Redis缓存中,此时Redis缓存中存储的还是原先的老数据
  • 线程一完成数据的修改操作
  • 此时数据库中的数据是修改后的新数据,而Redis缓存中的数据还是更新前的旧数据,此时就发生了Redis缓存和数据库中的数据不一致的情况

💡 如果先操作Redis缓存的话,就可能出现将Redis中的数据删除之后旧数据又被放入其中去,如果后期再有查询操作,那么在Redis缓存中查询到的数据就会全部是旧的数据,产生了缓存脏读,直到数据到了过期时间被清除掉,如果没有设置过期时间该数据就永远都是脏数据,不能保证Redis和数据库的一致性

先操作数据库

那我们先操作数据库呢?还会有这种问题的发生吗?
在这里插入图片描述

🔆先进行数据库操作流程

  • 线程一进行删除Redis缓存和修改数据库数据操作,线程二进行查询数据操作
  • 线程一首先对数据库中的数据进行修改
  • 在此期间如果出现了另外一个线程来查询数据,就会直接从Redis中读取旧数据返回
  • 线程一操作完数据库后对Redis缓存进行延迟删除
  • 接下来的查询操作在Redis中查询不到数据,会向数据库中查询,并将查询到的数据放入Redis缓存中,此时Redis和数据库中的数据都是修改后的新数据

💡 先删数据库再删缓存,在多线程的情况下,当一个线程删除数据库,另一个线程读取缓存数据,读到的是未修改的Redis缓存中的旧数据,当先前一个线程删除完数据库后就会更新缓存,此时Redis和数据库中的数据就可以一致了,只会产生一次脏读

如何保证数据的一致性

🍭此时我们可以看到,在只读场景中是不会出现数据不一致的情况的,只有在并发的写操作中才会出现。问题已经很清楚了,那么如何解决呢?

强一致性和弱一致性

🔆首先我们先来了解两个概念:强一致性和弱一致性:

  • 强一致性:任何一次读都能读到某个数据的最近一次写的数据;
  • 弱一致性:数据更新后,如果能容忍后续的访问,只能访问到部分或全部访问不到;

延时双删策略

如果我们先操作Redis再操作数据库的话,就会出现我们上面所说到的脏读的问题,要解决这个问题,我们可以使用延迟双删的策略:在更新MySQL之后,等待一个短时间(例如几秒)再尝试第二次删除,防止在这段时间内有请求重建缓存而造成数据不一致。

基于消息队列

使用消息队列(如RabbitMQ、Kafka等)异步处理数据更新,更新MySQL的同时将更新事件发送到队列,消费者收到消息后负责更新Redis缓存。

订阅MySQL binlog同步

Redis可以订阅MySQL的binlog日志,拿MySQL举例,当一条数据发生变更时,MySQL就会产生一条变更日志,我们可以订阅这个日志,拿到具体操作的数据,然后再根据这条数据去删除对应的缓存。

删除重试机制

使用删除重试机制,保证删除缓存成功。比如重试三次,三次都失败则记录日志到数据库并发送警告让人工介入。在高并发的场景下,重试最好使用异步方式,比如发送消息到mq中间件,实现异步解耦。

🏮以上的这些方法最多只能实现弱一致性🏮,也就是不能保证更新后每次读取到的数据都是最新数据,如果我们想要保证每次查询到的数据都是新的数据的话,即保证数据的强一致性,那就必须要保证他们的操作是原子性的,那么只能加一把锁了。但是加锁的话是会影响到系统的吞吐量,而我们使用Redis就是为了提高性能,如果为了保证数据的强一致性而加一把锁,那么就得不偿失了。

总结

  • 数据不一致的情况在只读场景中是不会出现的,只有在并发的写操作中才会出现;
  • 相对于直接删除Redis缓存来讲,修改缓存的成本更高,因此更加推荐修改数据库后直接删除Redis缓存,在下一次查询操作时再向数据库中拿到数据保存在Redis中;
  • 先操作Redis的话为了保证和数据库数据的最终一致性,需要进行延时双删的操作,即对Redis缓存进行两次删除,相比于先操作MySQL来说多进行一次删除Redis的操作;
  • 无论是先操作数据库还是先操作缓存,都会出现脏读的情况,因为redis和数据库不是原子性的,要想保证其原子性那么只有加锁,但是加锁的话又会影响性能;
  • Redis删除失败要进行重删操作

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第23章-OSPF基础

1. RIP协议的问题 2. OSPF概述 3. OSPF初始化流程 4. OSPF报文类型 5. OSPF分区域管理 1. RIP协议的问题 1)问题1 设计粗糙 2)问题2 环路问题:会产生环路 跳数限制:最大跳数受限,无法大规模组网 广播方式:广…

设计模式总结-抽象工厂模式

抽象工厂模式 模式动机模式定义模式结构模式分析模式实例与解析实例一:电器工厂 模式动机 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工…

微信小程序怎么制作?制作一个微信小程序需要多少钱?

随着移动互联网的快速发展,微信小程序已成为连接用户与服务的重要桥梁。它以其便捷性和易用性,为各类企业和个人提供了一个全新的展示和交易平台。那么,如何制作一个微信小程序?又需要投入多少资金呢?本文将为您提供全…

H5面临的网络安全威胁和防范措施

H5,是基于HTML5技术的网页文件。HTML,全称Hyper Text Markup Language,即超文本标记语言,由Web的发明者Tim Berners-Lee与同事Daniel W. Connolly共同创立。作为SGML的一种应用,HTML编写的超文本文档能够独立于各种操作…

java自动化-03-04java基础之数据类型举例

1、需要特殊注意的数据类型举例 1)定义float类型,赋值时需要再小数后面带f float num11.2f; System.out.println(num1);2)定义double类型,赋值时直接输入小数就可以 3)另外需要注意,float类型的精度问题…

Swing中的FlowLayout/WrapLayout在打横排列时候如何做到置顶对齐

前言 最近在开发swing客户端时候碰到一个棘手的问题: Swing中的FlowLayout/WrapLayout在打横排列时候如何做到置顶对齐如果是vue或者react,一搜百度什么都出来了,swing的话,嗯。。。资料有点少而且大部分是stack overflow上面的…

CEF的了解

(14 封私信 / 80 条消息) CEF和Electron的区别是什么? - 知乎 (zhihu.com) Electron面向的开发者:会用JavaScript,HTML,CSS,不会C CEF面向的开发者:会用JavaScript,HTML,CSS,会C (14 封私信 / 80 条消息) liulun - …

UI自动化测试重点思考(上)--元素定位/验证码/测试框架

UI自动化测试重点思考--元素定位 Selenium定位元素selenium中如何判断元素是否存在?定位页面元素webdriver打开页面id定位name定位class_name定位tag_name 定位xpath定位css_selector定位link_text 定位partial_link 定位总结 selenium中元素定位的难点?…

典型病例(第二期):血糖18.3,小便多且泡沫难消,滋阴补肾效果显著

案例分享: 患者廖先生,男,43岁 主诉:身体明显特别消瘦,血糖检测达到18.3,患者口干口渴,小便频繁,且带有很难消散的泡沫,疲倦乏力,多食易饥,每天…

MySQL 主从复制架构搭建及其原理

前言 系统的性能瓶颈一般出现在数据库上,以 mysql 为例,如果存在高并发的写请求,势必会有锁表,锁数据行的情况发生,这时候如果有读请求刚好访问到被锁的数据,那么读请求会阻塞,直到写请求处理完…

前端三剑客 —— CSS (第六节)

目录 内容回顾: 弹性布局属性介绍 案例演示 商品案例 布局分析 登录案例 网格布局 内容回顾: 变量:定义变量使用 --名称:值; 使用变量: 属性名:var(--名称)&a…

爬虫实战三、PyCharm搭建Scrapy开发调试环境

#一、环境准备 Python开发环境以及Scrapy框架安装,参考:爬虫实战一、Scrapy开发环境(Win10Anaconda)搭建 PyCharm安装和破解,参考:爬虫实战二、2019年PyCharm安装(激活到2100年) …

基于ArgoCD和Testkube打造GitOps驱动的Kubernetes测试环境

本文介绍了一项新工具,可以基于Gitops手动或者自动实现Kubernetes集群应用测试,确保集群的健康状态与Git仓库定义的一致。原文: GitOps-Powered Kubernetes Testing Machine: ArgoCD Testkube 简介:GitOps 云原生测试面临的挑战 现代云原生应…

SQLite的架构(十一)

返回:SQLite—系列文章目录 上一篇:SQLite下一代查询规划器(十) 下一篇:SQLite—系列文章目录 介绍 本文档介绍SQLite库的架构。 这里的信息对那些想要了解或 修改SQLite的内部工作原理。 接口SQL 命令处理器虚拟机B-树…

PHP 插值搜索(Interpolation Search)

给定一个由 n 个均匀分布值 arr[] 组成的排序数组,编写一个函数来搜索数组中的特定元素 x。 线性搜索需要 O(n) 时间找到元素,跳转搜索需要 O(? n) 时间,二分搜索需要 O(log n) 时间。 插值搜索是对实例二分搜索的改进,…

Api网关-使用Grafana可视化Apisix指标

文章目录 前言一、Apisix部署二、安装配置Grafana1. 安装Grafana2. 设置中文3. 启动4. 登录5. 启停命令5.1 启动和停止5.2 启禁用开机自启动5.3 查看状态 三、安装配置prometheus1. 安装2. 配置服务3. 启动4. 登录5. prometheus启停命令5.1 启动和停止5.2 启禁用开机自启动5.3 …

element-ui breadcrumb 组件源码分享

今日简单分享 breadcrumb 组件的源码实现,主要从以下三个方面: 1、breadcrumb 组件页面结构 2、breadcrumb 组件属性 3、breadcrumb 组件 slot 一、breadcrumb 组件页面结构 二、breadcrumb 组件属性 2.1 separator 属性,分隔符&#xff…

钉钉事件订阅前缀树算法gin框架解析

当钉钉监测到发生一些事件,如下图 此处举例三个事件user_add_org、user_change_org、user_leave_org,传统的做法是,我们写三个if条件,类似下图 这样字符串匹配效率比较低,于是联想到gin框架中的路由匹配算法&#xff0…

【大数据存储】实验4 NoSQL数据库

实验4 NoSQL数据库 NoSQL数据库的安装和使用实验环境: Ubuntu 22.04.3 Jdk 1.8.0_341 Hadoop 3.2.3 Hbase 2.4.17 Redis 6.0.6 mongdb 6.0.12 mogosh 2.1.0 Redis 安装redis完成 新建终端启动redisredis-server新建一个终端redis-cli 建表操作 尝…

随机生成Long全范围数

随机生成Long全范围数 前言实现思路主要代码分区随机生成过程案例:随机生成100个数 朴素的比较总结 前言 使用自带的Random.nextLong()函数生成Long型的长整数,范围比较小,如下图。100个随机数没看见10以内的数字。所以考虑实现随机化生成大…