MySQL 与 Redis 的数据一致性问题

      • 读数据的逻辑基本一致
      • 问题1: 一致性有哪些?
      • MySQL 与 Redis 的数据一致性方案有哪些?
          • 先写MySQL还是先写Redis?
          • 缓存数据是更新还是清除?
          • 强一致还是最终一致?
            • 问题: 如果mysql写成功了,但是Redis写(删除)失败了怎么办?
            • 重试机制的幂等问题如何解决?
        • 方案1: 先更新 MySQL 再清除 Redis
        • 方案2: 双删策略
        • 方案3: 监听MySQL的binlog日志删除
      • 问题: 热 key 失效 问题
          • 思路1: 让热key不丢
          • 思路2: 热key 失效的限流策略
            • **对于过期淘汰的解决方案**
            • **对于无法避免的热 key 失效**

读数据的逻辑基本一致

因为Redis有更好的性能(20w qps),通常做数据缓存使用,查询的时候

  • 先检查Redis,如果命中直接返回.
  • Redis未命中,再检查MySQL,
  • 将MySQL命中的数据同步到Redis,并且做返回

那么数据要更新,如何解决数据一致性问题?

问题1: 一致性有哪些?

  • 强一致: 更新数据同时生效(简单理解就是原子的,只要数据一改,无论怎么查询获取的都是最新的数据)
    • 强一致性实现的方案有哪些
      • 分布式事务
      • 共识算法(比如raft)
      • 但是因为强一致非常影响性能所以强一致方案很少使用
  • 弱一致: 提交更新之后允许一段时间内的数据不一致
    • 最终一致性:最终一致性是弱一致的一个,它允许更新后短时间内的数据不一致情况,但是最终会达成数据一致
    • 一般会使用重试或者补偿机制确保数据的最终一致性

MySQL 与 Redis 的数据一致性方案有哪些?

先写MySQL还是先写Redis?
  • 原则: 谁保存全量持久化数据先更新谁

为什么需要先写 MySQL?

  • 避免MySQL数据覆盖,丢失更新;(造成永久性错误)

如果先 操作缓存数据( Redis )有什么问题 ?

先更新/清除缓存(Redis)数据,再更新MySQL 的问题:

假设有两个连续的更改视频标题的请求,
请求 1改为 A; 请求 2改为 B

先写(更新/清除) Redis,两次清除 / 先改成 A,然后改成 B(Redis 是单线程的,请求将顺序执行)

这时候请求 1 的线程处理比较慢(或者阻塞了一下)

这时候请求 2 先更新了持久化全量数据(MySQL) 中记录:改为 B

然后请求 1 才开始更改MySQL 中的数据:改为 A

这时候 MySQL 的数据是错的,并且重启也无法恢复的错误
(因为 Redis 是缓存,如果数据不一致(Redis 数据不对)可以将 MySQL 数据刷到 Redis 也能达成一致,但是如果 MySQL 数据不对将无法修复)

这里只是改名字的例子,如果涉及到交易问题将更严重.

缓存数据是更新还是清除?

上面已经确定要先写 MySQL,再写 Redis

那么是更新还是清除呢?

  • 原则: 保证数据的一致性,一 般使用清除缓存的方式

还是上面的例子

如果更新缓存数据而不是删除存数据( Redis )有什么问题 ?

假设有两个连续的更改视频标题的请求,
请求 1改为 A; 请求 2改为 B

先更新 MySQL,先改成 A,然后改成 B

这时候请求 1 的线程处理比较慢(或者阻塞了一下)

这时候请求 2 先更新了 缓存数据( Redis) 中 的记录

然后请求 1 才开始更改 Redis 中的数据

这时候Redis 的数据是错误的,会导致后面查询的时候全部查询到错误的数据(只能重新加载 MySQL 数据到 Redis 才能恢复)

简单来讲,我们只能保证先到的请求的第一阶段写的执行顺序(MySQL 内部的事务),第二阶段写就无法保证执行顺序(除非使用强一致性方案),这时候如果使用更新 Redis 的方案就有数据错误的风险

强一致还是最终一致?

强一致

一般强一致实现是通过事务实现的

  • 开启一个mysql事务(start)

  • 操作mysql,更新数据(这里在事务提交之前都是会持续占有资源,其他请求要更改就会阻塞,直到事务提交)

  • 操作 Redis 删除(强一致也可以更新)数据

  • 提交事务(释放事务过程中的锁,让其他请求可以执行)

  • 基本原理就是借助mysql 事务的原子性来实现 mysql 与 Redis 数据的强一致性)

一般的场景:
银行,金融这种安全性特别高的场景会使用强一致性

最终一致

一般是异步任务,加上重试机制与补偿机制确保最终一致性

核心原理就是只要 mysql 更新成功了,就认为数据更新成功了,而缓存(Redis) 的更新通过异步任务去实现的

问题: 如果mysql写成功了,但是Redis写(删除)失败了怎么办?
  • 首先明确这是一个低概率事件,清除数据,没有任何复杂的逻辑,仅仅是清除,很少出现失败的问题

  • 一般会选择重试来解决偶然性(偶尔因为网络问题)的失败

如果重试一直失败怎么办?

  • 如果重试一直失败一般是 Redis 不可用了,或者服务端与Redis 的网络不可用了;
  • 这时候已经不是偶尔失败了,而是所有的(Redis)请求 将全部失败,这时候应该立即告警,并且限流,降级,熔断保护服务(mysql 等)不被打爆;
  • 然后立即抢修

所以设置一个最大重试次数,超过应该立即告警

重试机制的幂等问题如何解决?

幂等问题一般要通过唯一键验证来解决,比如点赞,那么就记录一下谁给谁点了赞,如果记录已经存在就不在增加点赞数量.

但是这边是重试 Redis 写(清除缓存的任务),重试不会产生幂等问题

  • Redis 不要更新缓存数据,而是清除缓存
方案1: 先更新 MySQL 再清除 Redis
  • 收到更新的请求,先更新(update) mysql 数据,
  • 如果更新完成就开一个线程做Redis 清除,
  • 同时做返回

好处实现简单

方案2: 双删策略

清除 Redis->更新mysql->再清除 Redis

  • 个人感觉很鸡肋(与第一种方案的效果相似,但性能更差)
方案3: 监听MySQL的binlog日志删除

单独开一个线程监听 mysql 的 binlog 日志,如果有更新,我们就对应的删除 Redis 对应的 key

我们的业务层只需要关系 mysql 的更新就可以了

  • 优势:业务逻辑更简单,同时避免反复创建与销毁线程带来的性能损耗
  • 但是需要 mysql 开启 binlog 日志(如果服务本身没有 binlog 的需求的话单独开会增加额外的消耗)

思考: 监听日志更新缓存数据行不行?

问题: 热 key 失效 问题

我们使用的是清除 Redis 的策略,那么如果数据是一个热点数据,有频繁的更新与查询会发生什么?

这种清除 Redis 的策略如果有频繁的更新对导致缓存层(Redis) 会失效, 大量的请求会打到 mysql 上面,mysql 可能直接被打爆,造成严重的事故.

(热 key 失效,缓存击穿问题)

场景:
假设现在是一个短视频的功能,有一个爆火的视频,用户疯狂的点赞,评论,收藏;
每一个操作都会更新视频的数据(点赞数,评论数,收藏数);
如果我们清除Redis 的缓存数据,所有的获取视频数据的请求都全部打到mysql,mysql 必被打爆.这时候怎么办?

两个思路:

思路1: 让热key不丢
  • 首先明确频繁更新的数据到底是什么?

    • 一般情况下频繁更新的数据都是计数类数据(观看量,点赞数,评论数,收藏数)这一类是频繁更新的数据;像什么内容,名称,简介,详情一般是不会做频繁更新的(谁家好人疯狂改自己的名字);

    • 针对计数类数据的方案就是,增量缓存,定时更新到 mysql 策略,避免数据频繁更新行为导致 Redis 缓存长期失效造成击穿

  • 具体做法

    • 计数类数据单独(Redis)缓存增量,然后定时刷到 mysql 中,而不是每次都更新数据
    • 查询的时候先查询 Redis 的原始数据(旧记录)与最近时间数据的增量(点赞,评论,收藏的增量),在服务层做计算统计,再返回给客户端,这样就可以避免频繁更新问题

如果在更新期间有查询怎么办?
逻辑是一样的(原始数据+增量)
因为我们更新完成(mysql 更新成功)同时清除 Redis 的缓存记录 并将数据的增量设置为 0(lua 脚本实现两个操作的原子性)

思路2: 热key 失效的限流策略

上面的方法可以减少热 key 失效的概率,但是这样是无法避免热 key 失效的.

还有两个热 key 失效的情景

  • 过期淘汰
  • 数据更新(比如定时刷新增量数据/作者更改了视频的详情(名字/简介/详情等))
对于过期淘汰的解决方案
  • 热key 不淘汰

具体做法
我们可以维护一个热 key 的数据有哪些

lfu : 一般使用一段时间 (1s 或者 1 分钟)key的访问次数 如果达到某个阀值(比如每秒访问超过 100 次的就算热 key)

对于这一类 key 不设置过期时间,等到热 key 不再热(低于 100 次时)就再次加上过期时间(避免不设置过期时间的 key 越来越多),这样避免热 key 失效问题

对于无法避免的热 key 失效

数据更新的清除缓存行为(定时的增量数据刷新/用户更改)

  1. 对用户进行限流: 比如用户每分钟只能改一次数据

  2. 标记限流策略:

具体做法

  • 如果查询 key 未命中 Redis,那么对改数据 key进行标记(使用 lua 脚本 对 key 储存一个状态-更新中…),后面的请求(在这个请求将数据同步到Redis 前)全部拒绝,

  • 然后这个请求去查询 mysql 并同步到Redis(覆盖 key 刚刚设置的状态)

  • 为了避免永久"更新中"问题,设置更新中状态的时候需要携带过期时间,避免查询途中服务器宕机导致数据状态一直处于更新中

参考:
tps://www.cnblogs.com/coderacademy/p/18137480
https://juejin.cn/post/6964531365643550751
https://www.cnblogs.com/huang580256/p/17299585.html
https://blog.csdn.net/weixin_45433817/article/details/130814075

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

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

相关文章

Qt WORD/PDF(五)使用Json一键填充Word表格

关于QT Widget 其它文章请点击这里: QT Widget 国际站点 GitHub: https://github.com/chenchuhan 国内站点 Gitee : https://gitee.com/chuck_chee 姊妹篇: 《Qt WORD/PDF(一)使用 QtPdfium库实现 PDF 操作》 《Qt WORD/PDF&#…

Elasticsearch入门学习

Elasticsearch是什么 Elasticsearch 是一个基于 Apache Lucene 构建的分布式搜索和分析引擎、可扩展的数据存储和矢量数据库。 它针对生产规模工作负载的速度和相关性进行了优化。 使用 Elasticsearch 近乎实时地搜索、索引、存储和分析各种形状和大小的数据。 特点 分布式&a…

spring cloud的核心模块有哪些

Spring Cloud 的核心模块就像一套精心设计的工具箱,每个模块都扮演着特定的角色,共同构建起微服务架构的坚实基础。 1. Spring Cloud Netflix(部分组件已迁移或弃用,但仍是理解 Spring Cloud 的重要参考): …

Linux创建server服务器实现多方信息收发

一,服务端 1.创建socket套接字,用于网络通信,同一台机器上的进程也可以通过本地套接字进行通信 //1.socket s_fd socket(AF_INET,SOCK_STREAM,0); if(s_fd -1){ perror("socket"); exit(-1); } //server address s_addr.sin_fami…

工厂人员定位管理系统方案(二)人员精确定位系统架构设计,适用于工厂智能管理

哈喽~这里是维小帮,提供多个场所的定位管理方案,如需获取工厂人员定位管理系统解决方案可前往文章最下方获取,如有项目合作及技术交流欢迎私信我们哦~撒花 在上一篇文章中,我们初步探讨了工厂人员定位管理系统的需求背景以及定位方…

金融项目实战 04|JMeter实现自动化脚本接口测试及持续集成

目录 一、⾃动化测试理论 二、自动化脚本 1、添加断言 1️⃣注册、登录 2️⃣认证、充值、开户、投资 2、可重复执行:清除测试数据脚本按指定顺序执行 1️⃣如何可以做到可重复执⾏? 2️⃣清除测试数据:连接数据库setup线程组 ①明确…

C++ ——— 内部类

目录 内部类的概念 内部类的特征 sizeof(外部类) 的大小 内部类的实例化 内部类就是外部类的友元 内部类的概念 如果一个类定义在另一个类的内部,这个内部类就叫做内部类,内部类是一个独立的类,它不属于外部类,更不能通过外…

ubuntu22.4 ROS2 安装gazebo(环境变量配置)

ubuntu版本:ubuntu22.4 最近在学习ROS2 视频教程古月居的入门课: 视频教程 文字笔记 问题 在学到关于Gazebo的时候,遇到下面问题: 运行 $ ros2 launch gazebo_ros gazebo.launch.py在这里卡住,不弹出gazebo 解决…

QT Quick QML 实例之椭圆投影,旋转

文章目录 一、前言二、演示三、部分代码与分析 QML 其它文章请点击这里: QT QUICK QML 学习笔记 国际站点 GitHub: https://github.com/chenchuhan 国内站点 Gitee : https://gitee.com/chuck_chee 一、前言 此 Demo 主要用于无人机吊舱视角的模拟&#xf…

Java-数据结构-栈与队列(常考面试题与单调栈)

在上一篇的学习中,我们学习了栈和队列的基本知识,以及它们对应都有哪些方法,在什么应用场景下如何使用,并且还对它们进行了模拟实现,而其实对于栈和队列的相关知识还远不止于此,而今天我们就对栈与队列进行…

【Docker】Docker部署多种容器

关于docker,Windows上使用Powershell/CMD执行指令,Linux系统直接使用终端执行指令。 docker安装MySQL 拉取MySQL 也可以跳过拉取步骤,直接run,这样本地容器不存在的话,会自动拉取最新/指定的版本。 # 默认拉取最新…

Apache Hop从入门到精通 第二课 Apache Hop 核心概念/术语

1、apache hop核心概念思维导图 虽然apache hop是kettle的一个分支,但是它的概念和kettle还是有一些区别的,下图是我根据官方文档梳理的appache hop的核心概念思维导图。 2、Tools(工具) 1)Hop Conf Hop Conf 是一个…

不同音频振幅dBFS计算方法

1. 振幅的基本概念 振幅是描述音频信号强度的一个重要参数。它通常表示为信号的幅度值,幅度越大,声音听起来就越响。为了更好地理解和处理音频信号,通常会将振幅转换为分贝(dB)单位。分贝是一个对数单位,能…

Apache JMeter 压力测试使用说明

文章目录 一、 安装步骤步骤一 下载相关的包步骤二 安装 Jmeter步骤三 设置 Jmeter 工具语言类型为中文 二、使用工具2.1 创建测试任务步骤一 创建线程组步骤二 创建 HTTP 请求 2.2 配置 HTTP 默认参数添加 HTTP消息头管理器HTTP请求默认值 2.3 添加 查看结果监听器2.4 查看结果…

在 Safari 浏览器中,快速将页面恢复到 100% 缩放(也就是默认尺寸)Command (⌘) + 0 (零)

在 Safari 浏览器中,没有一个专门的快捷键可以将页面恢复到默认的缩放比例。 但是,你可以使用以下两种方法快速将页面恢复到 100% 缩放(也就是默认尺寸): 方法一:使用快捷键 (最常用) Command (⌘) 0 (零…

Android Dex VMP 动态加载加密指令流

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/ 上一篇【详解如何自定义 Android Dex VMP 保护壳】实现了 VMP 保护壳。 为了进一步加强对 dex 指令的保护,实现指令流加密和动态加载,…

RabbitMQ故障全解析:消费、消息及日常报错处理与集群修复

文章目录 前言:1 消费慢2 消息丢失3 消息重复消费4 日常报错及解决4.1 报错“error in config file “/etc/rabbitmq/rabbitmq.config” (none): no ending found”4.2 生产者发送消息报错4.3 浏览器打开IP地址,无法访问 RabbitMQ(白屏没有结…

Windows图形界面(GUI)-QT-C/C++ - QT控件创建管理初始化

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 控件创建 包含对应控件类型头文件 实例化控件类对象 控件设置 设置父控件 设置窗口标题 设置控件大小 设置控件坐标 设置文本颜色和背景颜色 控件排版 垂直布局 QVBoxLayout …

Java Web开发进阶——错误处理与日志管理

错误处理和日志管理是任何生产环境中不可或缺的一部分。在 Spring Boot 中,合理的错误处理机制不仅能够提升用户体验,还能帮助开发者快速定位问题;而有效的日志管理能够帮助团队监控应用运行状态,及时发现和解决问题。 1. 常见错误…

B+树的原理及实现

文章目录 B树的原理及实现一、引言二、B树的特性1、结构特点2、节点类型3、阶数 三、B树的Java实现1、节点实现2、B树操作2.1、搜索2.2、插入2.3、删除2.4、遍历 3、B树的Java实现示例 四、总结 B树的原理及实现 一、引言 B树是一种基于B树的树形数据结构,它在数据…