【redis-05】redis保证和mysql数据一致性

redis系列整体栏目


内容链接地址
【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325
【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756
【三】redis缓存穿透、缓存击穿、缓存雪崩https://zhenghuisheng.blog.csdn.net/article/details/142577507
【四】redisson实现分布式锁实战和源码剖析https://zhenghuisheng.blog.csdn.net/article/details/142646301
【五】redis保证和mysql数据一致性https://zhenghuisheng.blog.csdn.net/article/details/142687101

如需转载,请输入:https://blog.csdn.net/zhenghuishengq/article/details/142687101

redis保证和mysql数据一致性

  • 一,redis保证和mysql数据一致性
    • 1,数据不一致原因
      • 1.1,更新缓存出现的问题
      • 1.2,删除缓存出现的问题
      • 1.3,理想情况
    • 2,数据不一致解决方案
      • 2.1,延迟双删(不推荐)
      • 2.2,消息队列
      • 2.3,分布式锁
      • 2.4,canal同步

一,redis保证和mysql数据一致性

在项目中,redis一般作为缓存使用,从而加快系统的读写效率和吞吐量,但是数据最终一般是存储在mysql中,因此在实际开发中,尤其是在高并发的互联网项目中,经常会遇到这种mysql和redis数据不一致的情况。如在一个电商的扣减库存的场景中,如果redis中的数据和mysql中的数据没有保证一致,那么就很有可能造成超卖的问题,那么平台就可能会因为这个问题造成商家的损失

1,数据不一致原因

数据不一致原因无非就是那几种原因,在分布式场景中,并发带来的问题,网络抖动带来的问题,分布式事务不一致问题等。

1.1,更新缓存出现的问题

如下面这个场景,以扣减库存的场景为例,假设有100个库存,此时多个线程对该商品下单。如在一个4s的时间线内,线程1和线程2先后下单,在不考虑mysql事务的情况下,来查看以下会出现的问题,其流程如下:

  • 首先在第一秒时线程1进行一个扣减库存操作,但是由于网络卡顿的原因,导致在第4秒才更新redis的缓存只为95,并且写入mysql的数据也为95;
  • 在第二秒时线程2也下单10件商品,此时redis缓存还是100,因此操作的还是这个100,那么扣减库存之后的值变成90,然后在第三秒时更新缓存值为90;
  • 由于暂时不考虑事务,因此此时mysql的值为100-10-5为85,而redis中的缓存值由于覆盖的问题导致还是95,此时就出现了mysql和redis值不一致的问题。

在这里插入图片描述

1.2,删除缓存出现的问题

更新缓存指的是在写完mysql数据之后立马就去更新缓存,效率可能会低一些,除了更新缓存还可以删除缓存,就是在写的时候不更新缓存,而是直接删掉缓存,再读数据的时候进行redis缓存数据的写入,如以下流程

  • 首先线程1在第一秒时扣减5个库存,然后将缓存删除,此时redis中的值为95
  • 第二秒一个线程3来读数据,发现缓存没有就读数据库,此时数据库的值为95
  • 在第三秒有一个用户下单,扣减库存操作为10,此时mysql中为85,删除缓存
  • 由于卡顿问题,在第四秒线程3才执行更新缓存操作,但是拿的值不是最新值,导致原本redis值为85的,由于覆盖的问题此时redis的值为95,导致redis和mysql数据不一致的问题

在这里插入图片描述

1.3,理想情况

在理想情况下,我们更希望的是每个命令都可以顺序执行,将各种无法预料的并发问题直接改成串行化操作。当然如果在此基础上进行优化的话,在加锁的同时,考虑加一把互斥锁,从而保证读读共享,其他互斥的操作

在这里插入图片描述

2,数据不一致解决方案

2.1,延迟双删(不推荐)

延迟双删顾名思义,就是延迟一段时间,删除某些线程因为卡顿原因导致更新缓存的值出错,如下图线程3由于卡顿,造成缓存覆盖的问题,从而导致了redis中缓存和mysql中的值出现不一致的问题,但是通过延迟双删的方式,对缓存做两次删除,并且两次删除间设置一定的时间间隔,从而解决部分卡顿问题

在这里插入图片描述

其伪代码如下,首先是第一次删除缓存,然后休眠个300秒,然后再次删除缓存。

#第一次删除缓存
redis.delKey(phone:1001)
#休眠300毫秒
Thread.sleep(300)
#再删除缓存
redis.delKey(phone:1001)

延迟双删的方式也不能完全解决这种数据不一致问题,只能减少这种数据不一致概率的事件发生,因为不能保证某些查询的线程要卡顿多久,比如我设置300毫秒,但是有的线程卡顿一秒,因此也不能够完全解决。

其次所有的写操作都得删除两次,这对redis来说会有一定的性能影响,除了本身的操作之外,还得额外的增加一段延时的时间,对于使用redis的初衷来说就没那么友好,更加的适用于 读多写少 的场景,如商品首页等。

2.2,消息队列

如果为了强一致性,那么可以直接使用消息队列来使用,其本质也很简单,将并行的操作,全部加入到消息队列中串行执行。

消息队列确实可以解决这种数据不一致问题,因为都串行执行,不会有那种并行和并发的问题。但是使用消息队列需要额外的引入中间件,还需要考虑数据的持久化,不丢失,以及顺序消费等问题,如果整个系统是已经有再使用消息中间件的话,如kafka、rocketMq这些,那么是可以考虑使用这种方式的

在这里插入图片描述

2.3,分布式锁

在上一篇中,讲解了redis分布式锁的使用和底层原理,分布式锁的机制和上面的消息队列的机制一样,都是将并行执行的操作方式改成串行的执行方式,直接通过redisson实现的分布式锁即可

RLock lock = redisson.getLock(PHONE_ID);
lock.lock();
lock.unlock();

通过这种分布所锁机制,可以保证数据的串行执行,不出现redis数据脏写问题和覆盖问题

在这里插入图片描述

当然也可以通过redis的 读写锁 进行优化,部分代码如下,直接在读的接口中使用读锁,使用后需要释放锁

//获取一把读写锁
RReadWriteLock readWriteLock = redissoon.getReadWriteLock(lockKey);
//获取读锁
RLock readLock = readWriteLock.readLock();
//加锁
readLock.lock();
//解锁
readLock.unlock();

写的接口中使用写锁即可,使用后也许把锁释放。

//获取一把读写锁
RReadWriteLock readWriteLock = redissoon.getReadWriteLock(lockKey);
//获取写锁
RLock writeLock = readWriteLock.writeLock();
//加锁
writeLock.lock();
//解锁
writeLock.unlock();

不管是原生的Redission锁还是这种读写锁,底层都是通过lua脚本实现,这种方式也更加的适用于 读多写少 的场景。相对于消息队列,可以选这种方式实现

2.4,canal同步

上面几种方式都是需要通过手动的删除缓存或者更新缓存的数据实现数据同步,除了上面几种方式之外,还可以用阿里的canal开源中间件来实现数据的同步,并且通过这种方式,不需要认为的去操作缓存

其原理也很简单,就类似于mysql的主从复制原理,伪装成一个从结点,监听mysql的binlog日志,然后将执行的mysql的操作,通过这个canal中间件同步到redis中。

在这里插入图片描述

这样的话,也不需要去考虑redis的更新和删除操作,只需要查即可,查不到再查mysql。

当然如果想直接使用这种canal中间件的话,需要手动的重新部署,也相当于重新引入了一个新的中间件。如果数据量大的话,可以考虑使用这种方式来解决数据不一致问题。

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

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

相关文章

LeetCode hot100---双指针专题(C++语言)

双指针 (1)快慢双指针 适用于使用双指针进行元素移动,覆盖(2)首尾双指针 计算区域面积,三数之和1、移动0 (1)题目描述以及输入输出 (1)题目描述: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾&#…

不只是前端,后端、产品和测试也需要了解的浏览器知识(一)

目录标题 一、我们为什么要了解浏览器?1. 对于前端开发者2. 对于后端开发者 二、浏览器发展概述1. 宏观发展2. 微观发展 三、浏览器核心部件1. 浏览器界面介绍2. 目前浏览器的使用的渲染引擎和解释器总结3. 浏览器的解释器 四、各家浏览器目前的市场占比五、整体总结…

Linux基础命令date详解

date 是一个用于显示和设置系统日期与时间的命令。它可以以多种格式输出当前的日期和时间。以下是 date 命令的常用参数及使用示例。 基本用法 date [选项] [格式] 常用参数详解 -u, --utc, --universal 使用协调世界时(UTC)显示日期和时间。 示例: …

索尼MDR-M1:超宽频的音频盛宴,打造沉浸式音乐体验

在音乐的世界里,每一次技术的突破都意味着全新的听觉体验。 索尼,作为音频技术的先锋,再次以其最新力作——MDR-M1封闭式监听耳机,引领了音乐界的新潮流。 这款耳机以其超宽频播放和卓越的隔音性能,为音乐爱好者和专…

tornado

Tornado通过使用非阻塞网络I/O,可以扩展到数以万计的开放链接,非常适合 长时间轮询,WebSockets和其他需要与每个用户建立长期连接的应用程序。 特点 注重性能优越,速度快解决高并发异步非阻塞websockets 长连接内嵌了HTTP服务器…

速盾:免备案服务器?

速盾是一家提供网络安全服务的公司,其主要产品包括CDN加速、WEB防护、WAF、DDoS防护等。在网站建设过程中,选择一个合适的服务器是非常重要的一步。传统的服务器需要备案,涉及到较多的流程和审批时间,给网站运营带来了一定的麻烦。…

04 B-树

目录 常见的搜索结构B-树概念B-树的插入分析B-树的插入实现B树和B*树B-树的应用 1. 常见的搜索结构 种类数据格式时间复杂度顺序查找无要求O(N)二分查找有序O( l o g 2 N log_2N log2​N)二分搜索树无要求O(N)二叉平衡树无要求O( l o g 2 N log_2N log2​N)哈希无要求O(1) 以…

安全服务面试总结

154.mysql 安全要如何做? Mysql 账户权限安全 第 61 页 共 152 页 Mysql 数据的网络安全配置 密码策略安全 Mysql 日志 Mysql 数据库服务所在主机安全配置部署 SQL 注入检测、防御模块 mysqld 安全相关启动选项 mysql 备份策略 155.sqlserver public 权…

Python 循环跳出模式

Python 循环跳出模式 在 Python 编程中,循环是实现重复任务的重要工具。通常,我们会使用 for 或 while 循环来遍历序列或执行特定操作。然而,有时我们需要在特定条件下提前终止循环,这就是循环跳出的机制。Python 提供了几种方式…

IO模型介绍

一、理解IO 网络通信的本质就是进程间通信,进程间通信本质就是IO TCP中的IO接口:read / write / send / recv,本质都是:等 拷贝 所以IO的本质就是:等 拷贝 那么如何高效的IO? 减少“等”在单位时间的…

jenkins配置eureka、nacos发布优雅上下线服务

eureka发布期间优雅上下线 1、编写eureka下线脚本 vim biz_out_of_service-eureka.pyimport sys import requests#服务名,脚本第一个参数 APP_NAMEsys.argv[1] # 需要置为OUT_OF_SERVICE的服务实例的ID,脚本第二个参数 INSTANCE_IDsys.argv[2]# Eureka…

维修保养记录接口-维修保养记录API-汽车接口

维修保养记录接口的使用主要涉及到API对接和在线查询两种方式。以下是详细的使用步骤和注意事项: 一、API对接 注册与申请: 首先,你需要在提供维修保养记录接口的平台(如挖数据平台、第三方数据服务商等)进行注册&…

CORDIC算法笔记整理

CORDIC算法有两种模式,分别为旋转模式和向量模式。而在数字硬件实现混频处理时,CORDIC算法是比较好的方法,使用的是CORDIC的旋转模式,只需通过移位操作和加法就可以实现频谱搬移的乘法操作。 1 CORDIC算法理解 1.1 单次旋转 对…

SpringCloud学习记录|day1

学习材料 2024最新SpringCloud微服务开发与实战,java黑马商城项目微服务实战开发(涵盖MybatisPlus、Docker、MQ、ES、Redis高级等) 学redis讲到微服务就停了,nginx也是。 所以嘛,我终于来到微服务了。 复习MyBatisP…

CMU 10423 Generative AI:lec14(Vision Language Model:CLIP、VQ-VAE)

文章目录 1 概述2 CLIP (Used in GPT-V)3 VQ-VAE (Used in Gemini)**VQ-VAE 详细笔记****VQ-VAE 的模块组成与数据流** **1. 输入数据****2. 编码器(Encoder)****2.1 编码器的作用****2.2 数据流与维度变化****2.3 编码器输出** **3. 量化器(…

Go基础学习09-多协程资源竞争、sync.Mutex、sync.Cond、chan在多协程对共享变量的资源竞争中的使用

文章目录 Go中协程基础小记协程基础为什么需要多协程多协程面临的问题 代码演示存在共享变量的资源竞争Mutex解决资源竞争Mutex基本介绍代码演示 MutexCond解决资源竞争和CPU空转Cond条件变量讲解代码演示代码片段关于wait和for循环的解释说明条件变量Signal方法和Broadcast方法…

C++七种异常处理

在C++中,使用异常机制可以提高程序的健壮性和可维护性。异常是在程序运行时发生的一个事件,它会打断正在执行的程序的正常流程。C++异常处理机制可以使程序在出现异常时,进行异常处理,而不是退出程序。 基本的异常处理 #include <iostream> using namespace std;int …

IP 数据包分包组包

为什么要分包 由于数据链路层MTU的限制,对于较⼤的IP数据包要进⾏分包. 什么是MTU MTU相当于发快递时对包裹尺⼨的限制.这个限制是不同的数据链路对应的物理层,产⽣的限制. • 以太⽹帧中的数据⻓度规定最⼩46字节,最⼤1500字节,ARP数据包的⻓度不够46字节,要在后⾯补填 充…

云栖实录 | 开源大数据全面升级:Native 核心引擎、Serverless 化、湖仓架构引领云上大数据发展

本文根据2024云栖大会实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a; 王 峰 | 阿里云智能集团研究员、开源大数据平台负责人 李 钰&#xff5c;阿里云智能集团资深技术专家 范 振&#xff5c;阿里云智能集团高级技术专家 李劲松&#xff5c;阿里云…

[论文笔记] LLaMA3.2

https://github.com/meta-llama/llama-stack [2407.21783] The Llama 3 Herd of Models Meta 部落格資訊:https://ai.meta.com/blog/llama-3-2-connect-2024-vision-edge-mobile-devices/HuggingFace: