如何保证Redis与Mysql双写一致性?
https://www.cnblogs.com/coderacademy/p/18137480
延迟双删
对于上面链接的文章,里面的延迟双删没有给出具体的例子,也没有直接指出具体解决的问题是针对那种缓存策略,这里补充一下,延时双删缓存针对的是Cache aside pattern(缓存旁路策略),处理的是在高并发读写同时存在的情况下可能会出现的问题,详细如下。
什么是延迟双删:
延时双删策略能够有效解决缓存和数据库之间的数据不一致问题。它的核心思想是在更新数据库之后,先删除缓存中的数据,延迟一段时间后再次删除缓存中的数据。其具体步骤如下:
- 更新数据库:先将数据更新到数据库中。
- 删除缓存:立即删除缓存中对应的旧数据。
- 延迟一段时间:等待一段时间(通常是足够长以保证并发写入完成的时间)。
- 再次删除缓存:再次删除缓存中的数据,以防止并发操作在缓存中留下旧数据。
tips:延迟双删需要由更新数据的那个程序去处理。
延时双删策略的应用场景
延时双删策略的应用场景通常涉及**高并发写操作**和**读取操作**同时发生的情况。以下是一个典型的具体场景,展示如何出现缓存和数据库数据不一致的问题。
### 场景描述
假设有一个电商网站,使用缓存(如 Redis)来加速商品信息的读取。例如,一个商品的库存信息存储在数据库中,同时也缓存到 Redis 中,以便快速读取。
#### 具体流程
1. **用户A请求读取商品库存信息:**
- 用户A请求读取某个商品的库存信息,系统会优先从缓存(Redis)中读取该商品的库存。如果缓存中存在,则直接返回;如果不存在,则从数据库读取并将结果缓存起来。
2. **用户B请求更新商品库存信息:**
- 在用户A读取缓存中的商品库存时,用户B执行了一个购买操作,该操作会更新商品的库存信息。系统会先更新数据库中的商品库存,然后删除缓存中的商品库存信息,以确保下一次读取时会从数据库中获取最新数据。
#### 问题出现
在用户B更新商品库存后,但**在删除缓存之后、更新数据库完成之前的时间窗口**内,用户A再次读取商品库存信息。这会出现以下问题:
- 用户B更新库存后,缓存被删除,但是用户A此时发起读取请求,因为缓存已经被删除,系统会去数据库中读取库存数据。
- 但数据库还没有完成更新操作(可能因为写入操作较慢,或者在执行事务),用户A读取到的仍然是旧的库存信息。
- 最后,数据库更新完成,数据正确,但用户A刚刚读取到了错误(旧)的库存数据,导致**数据不一致**。
### 延时双删策略如何解决这个问题
为了防止上述数据不一致情况的发生,可以使用延时双删策略:
1. **用户B更新商品库存信息:**
- 更新数据库中的库存信息。
- **立即删除缓存**中的商品库存信息。
2. **用户A读取商品库存信息:**
- 如果在缓存被删除之后读取,系统会从数据库中读取。
- 用户A读取时可能得到旧数据(数据库尚未更新完成),但接下来的延时操作将解决这个问题。
3. **延迟删除缓存:**
- 设置一个延迟(例如 500 毫秒),在此延迟之后,再次尝试删除缓存中的商品库存信息。
- 这段延迟时间应足够长,以确保数据库更新操作已经完成。
### 具体应用中的时间点
- **T1:用户B请求更新库存,系统开始更新数据库。**
- **T2:用户B更新数据库后,立即删除缓存。**
- **T3:数据库更新操作未完成,用户A读取库存,发现缓存不存在,转向读取数据库。**
- **T4:用户A读取到旧的库存数据(数据库写操作未完成)。**
- **T5:延迟一段时间(如500毫秒),再次删除缓存。**
- **T6:用户A再次请求,发现缓存不存在,此时数据库已更新,读取到最新库存信息。**
### 为什么这个策略有效
1. **避免读取旧数据**:通过在更新数据库后立即删除缓存,避免缓存中存在旧数据。
2. **降低不一致的窗口期**:延迟删除缓存提供了一个补偿机制,以防止在数据库写入完成前的缓存穿透现象。
3. **高效读取最新数据**:第二次删除缓存确保了之后的读取操作总能获取最新数据,减少了缓存和数据库之间的数据不一致的风险。
### 总结
延时双删策略主要解决的是在缓存删除和数据库更新之间的短时间窗口内出现数据不一致的问题,特别适用于**高并发写操作和读操作混合**的场景,如电商库存更新、金融交易系统的账户余额更新等。通过这种策略,系统能够更好地保障缓存和数据库之间的数据一致性。
延迟双删除golang代码示例
package mainimport ("context""fmt""time""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options"
)func main() {// 示例:更新数据库并执行延时双删策略// 示例的数据库操作ctx := context.Background()// 第一步:更新数据库updateDatabase(ctx)// 第二步:立即删除缓存deleteCache()// 第三步:设置一个延迟任务(如 500 毫秒)time.AfterFunc(500*time.Millisecond, func() {deleteCache() // 再次删除缓存})fmt.Println("Database update and delayed cache deletion scheduled.")
}func updateDatabase(ctx context.Context) {// 示例数据库更新操作fmt.Println("Updating database...")// 在此处执行数据库的更新操作...
}func deleteCache() {// 示例缓存删除操作fmt.Println("Deleting cache...")// 在此处执行缓存删除操作...
}
Read-Through 和Cache Aside Pattern的读有什么区别
**Read-Through** 和 **Cache Aside Pattern**(又称为 **Lazy Loading** 或 **Lazy Caching**)是两种常见的缓存策略,它们在缓存读取(读)操作的实现上有一些关键区别。
### Read-Through 缓存策略
**Read-Through** 是一种由缓存层自动管理数据加载的策略,读操作首先检查缓存,如果缓存中没有数据,缓存层会自动从后端数据源(例如数据库)加载数据并将其存储到缓存中,然后返回给调用者。
#### 工作原理
1. **应用程序请求数据**:
- 应用程序向缓存层请求数据。
2. **缓存层检查数据**:
- 如果缓存中有数据,直接返回。
- 如果缓存中没有数据,缓存层会自动从后端数据源(如数据库)加载数据。
3. **缓存层更新缓存**:
- 将从后端数据源获取的数据存储到缓存中,以便下次快速访问。
4. **返回数据**:
- 缓存层返回数据给应用程序。
#### 特点
- **自动加载数据**:缓存层自动处理缓存未命中的情况,将数据从后端数据源加载到缓存中。
- **透明性**:应用程序不需要关心数据从哪里来,缓存层会自动管理。
- **常用于缓存代理中**:例如,使用特定的缓存中间件或服务来管理缓存。
#### 例子
如使用 AWS ElastiCache 或 Memcached 的集成模式,其中缓存中间件自动处理数据加载和缓存更新。
### Cache Aside Pattern 缓存策略
**Cache Aside Pattern**(Lazy Loading)是一种由应用程序主动管理缓存的数据加载的策略。应用程序在读取数据时首先检查缓存,如果缓存未命中,应用程序会主动从后端数据源加载数据并手动将数据写入缓存。
#### 工作原理
1. **应用程序请求数据**:
- 应用程序首先检查缓存中是否有数据。
2. **缓存未命中时应用程序加载数据**:
- 如果缓存中没有数据,应用程序从后端数据源(如数据库)加载数据。
3. **应用程序更新缓存**:
- 应用程序将从后端数据源获取的数据写入缓存。
4. **返回数据**:
- 应用程序返回数据给调用者。
#### 特点
- **主动管理缓存**:应用程序负责检查缓存、加载数据和更新缓存。
- **灵活性**:应用程序可以决定何时加载和更新数据,缓存的逻辑在应用程序中控制。
- **常用于手动控制缓存的场景**:例如,通过代码来管理缓存操作。
#### 例子
在 Go、Java 或 Python 应用中,程序员在业务逻辑中手动管理从缓存中读取数据,未命中时从数据库加载数据,并将其写入缓存的操作。
### 区别
| 特性 | Read-Through | Cache Aside Pattern |
|-------------------------------|-------------------------------------------------------|-----------------------------------------------------|
| **数据加载责任** | 缓存层自动负责从后端加载数据 | 应用程序负责加载数据和更新缓存 |
| **实现难度** | 较低,缓存层自动管理数据加载 | 较高,应用程序需要管理缓存逻辑 |
| **灵活性** | 较低,缓存策略由缓存层定义 | 较高,应用程序可以控制何时加载和更新数据 |
| **常见使用场景** | 通常用于缓存中间件或代理(如 Memcached, AWS ElastiCache)| 手动管理缓存的应用程序 |
| **缓存未命中后的开销** | 缓存层负责处理加载,应用程序不感知 | 应用程序处理加载逻辑,有可能影响性能 |
| **读写操作的复杂度** | 读操作简单,缓存层透明处理 | 读操作复杂,需要在应用中显式处理缓存和数据库访问 |
### 总结
- **Read-Through** 更适合希望透明缓存管理的场景,使用缓存中间件或代理自动处理数据加载,简化应用逻辑。
- **Cache Aside Pattern** 更适合需要灵活控制缓存逻辑的场景,应用程序可以根据业务需求主动决定何时加载和更新缓存。
相关文章:

如何保证Redis与Mysql双写一致性?
https://www.cnblogs.com/coderacademy/p/18137480 延迟双删 对于上面链接的文章,里面的延迟双删没有给出具体的例子,也没有直接指出具体解决的问题是针对那种缓存策略,这里补充一下,延时双删缓存针对的是Cache aside pattern(缓…...

9.8笔试记录
1.在c中哪些运算符不能重载? 在 C 中,有以下几个运算符不能被重载: . :成员访问运算符。例如obj.member中的.不能被重载。 :: :作用域解析运算符。用于指定命名空间、类等的作用域,不能被重载。 ?: ࿱…...

SRE-系统管理篇
SRE-系统管理篇 进程管理 进程的概念: 运行起来的程序,命令,服务等等都可以称作进行,进程都是运行在内存当中的。 程序的概念: 一般指安装包,程序代码,应用它们存放在磁盘上面的。 守护进程的概念: 守护进程,一直运行的进程,也可以叫做服务。 进程的分类 僵…...
傅里叶级数,傅里叶变换
先读文章:傅里叶分析之掐死教程(完整版)更新于2014.06.06 - 知乎 (zhihu.com) 傅里叶级数 一、内容:每个周期性函数都可以表示为无穷多个不同频率的正弦函数的叠加。 二、公式: 三、从时域到频域所保留的三点信息&…...

零知识证明在BSV网络上的应用
发表时间:2023年6月15日 2024年7月19日,BSV区块链主网上成功通过使用零知识证明验证了一笔交易。 零知识证明是一种技术,它允许一方(证明者)在不透露任何秘密的情况下,向另一方(验证者&…...

无任何门槛!3分钟5步,发布属于你的第一个智能体小程序,99%的人还不知道怎么用
相信大家都用微信小程序,但是大部分人应该还没有过属于自己的小程序吧。 今天程哥就带大家花三分钟用五步,来创建一个属于自己的微信小程序。 之前Coze在发布渠道里也有发布小程序的渠道,但是试过的人都知道,这个是有一定门槛的…...

怎么强制撤销excel工作表保护?
经常不是用的Excel文件设置了工作表保护,偶尔打开文件的时候想要编辑文件,但是发现忘记了密码,那么这种情况,我们怎么强制撤销excel工作表保护?今天分享两种解决方法。 方法一、 将excel文件转换为其他文件格式&…...

每天学习一个字符串类函数之memmove函数
目录 前言: 一、头文件 二、memmove函数的作用 三、理解memmove函数的定义 1、返回类型 2、参数 四、使用memmove函数 案例1: 案例2: 五、解决数据拷贝之前被覆盖的方法 六、模拟实现memmove函数 前言: 上一篇博客,我…...

【机器人工具箱Robotics Toolbox开发笔记(十三)】三自由度机器人圆弧轨迹规划仿真实例
在实际应用场景中,我们通常预先明确了目标末端的运动轨迹,随后引导机器人进行相应的动作。本实例具体展示了如何基于给定的两个点,计算出末端的精确位姿,并以此为基础,进一步规划出一条平滑的圆弧轨迹供机器人执行。这样的流程确保了机器人能够沿着预定的路径,精准且高效…...

软件工程-图书管理系统的概要设计
软件概要设计说明书 目录 软件概要设计说明书 一、引言 1.1 编写目的 1.2 背景 1.3 定义 1.3.1特定对象 1.3.2专业术语 1.4 参考资料 二、总体设计 2.1 需求规定 2.1.1信息要求 2.1.2功能要求 2.2 运行环境 2.3 基本概要设计和处理流程 2.4 体系结构设计 2.5 模…...

springboot 整合swagger
没有多余废话,就是干 spring-boot 2.7.8 springfox-boot-starter 3.0.0 结构 POM.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/…...

Flutter 进阶:绘制加载动画
绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 Load…...

【深度学习】梯度下降法
梯度就是导数,而梯度下降法就是一种通过求目标函数的导数来寻找目标函数最小化的方法。梯度下降目的是找到目标函数最小化时的取值所对应的自变量的值,目的是为了找自变量X。 最优化问题在机器学习中有非常重要的地位,很多机器学习算法最后都…...

基于机器学习的电商优惠券核销预测
1. 项目简介 随着移动互联网的快速发展,O2O(Online to Offline)模式已成为电商领域的一大亮点。优惠券作为一种有效的营销工具,被广泛应用于吸引新客户和激活老用户。然而,传统的随机投放方式往往效率低下,…...

PHP-FPM 远程代码执行漏洞(CVE-2019-11043)复现
启动环境 切换目录到vulhub/php/CVE-2019-11043下 查看端口 访问 安装漏洞利用工具 git clone https://github.com/neex/phuip-fpizdam.git 安装go语言 # 1、下载go,这里使用 go1.22.5 版本,可替换为最新版本 wget https://dl.google.com/go/go1.22.5.…...

Rust : 从事量化的生态现状与前景
Rust适不适合做量化工作? 一般地认为,目前大部分场景策略开发最佳是Python;策略交易和部署是C。但还是有人会问,Rust呢? 这个问题不太靠谱! 适不适合做一件事情,本身就是一件主观的事。即使是…...

Java项目——苍穹外卖(一)
Entity、DTO、VO Entity(实体) Entity 是表示数据库表的对象,通常对应数据库中的一行数据。它通常包含与数据库表对应的字段,并可能包含一些业务逻辑。 DTO(数据传输对象) 作用:DTO 是用于在…...

20240908 每日AI必读资讯
新AI编程工具爆火:手机2分钟创建一个APP! - AI初创公司Replit推出的智能体——Replit Agent。开发环境、编写代码、安装软件包、配置数据库、部署等等,统统自动化! - 操作方式也是极其简单,只需一个提出Prompt的动作…...

HNU-2023电路与电子学-实验3
写在前面: 本次实验是完成cpu设计的剩余部分,整体难度比上一次要小,细心完成就能顺利通过全部测评 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能…...

html基础语法 看这一篇就够了!
HTML 一 概念 html:html 文件根标签 head:编写页面相关的属性 title:页面标题 body:页面内容展示信息 二 DOM 树: 所有的标签都是 html 的子标签 head 和 body 是兄弟标签,同一级别 head 和 title 为父子标签 1.第一个程序 <html><head>…...

【redis】redis的特性和主要应用场景
文章目录 redis 的特性在内存中存储数据可编程的扩展能力持久化集群高可用快 redis 的应用场景实时数据存储缓存消息队列 redis 的特性 redis 的一些特性(优点)成就了它 在内存中存储数据 In-memory data structures MySQL 主要是通过“表”的方式来…...

部署后端WebSocket服务到AWS云服务器
目录 1.创建AWS账户2.选择EC2实例3.配置EC2实例4.使用VSCode连接到EC2实例5.部署WebSocket服务6.配置域名和SSL(可选)7.监控和维护 1.创建AWS账户 如果你还没有AWS账户,你需要先在AWS官网注册一个。 2.选择EC2实例 登录到AWS管理控制台。搜…...

常见的集合
1、Collection 单列集合的根接口 遍历方法 Collection<String> c new ArrayList<>(); c.add("赵敏"); c.add("小昭"); c.add("素素"); c.add("灭绝"); System.out.println(c); //[赵敏, 小昭, 素素, 灭绝]//1、迭代器遍…...

Swift知识点---RxSwift学习
1. 什么是RxSwift RxSwift是Swift函数响应式编程的一个开源库,由Github的ReactiveX组织开发、维护 RxSwift的目的是:让数据/事件流 和 异步任务能够更方便的序列化处理,能够使用Swift进行响应式编程 RxSwift本质上还是观察者模式ÿ…...

驾驭不断发展的人工智能世界
从很多方面来看,历史似乎正在重演。许多企业正争相采用生成式人工智能 (Gen AI),就像它们争相采用云计算一样,原因也是一样的:效率、成本节约和竞争优势。 然而,与云一样,GenAI 仍是一项发展中的技术&…...
冒泡排序——基于Java的实现
简介 冒泡排序(Bubble Sort)是一种简单的排序算法,适用于小规模数据集。其基本思想是通过重复遍历待排序的数组,比较相邻的元素并交换它们的位置,以此将较大的元素逐步“冒泡”到数组的末尾。算法的名称源于其运行过程…...

Mendix 创客访谈录|Mendix赋能汽车零部件行业:重塑架构,加速实践与数字化转型
在当前快速发展的技术时代,汽车行业正经历着前所未有的数字化转型。全球领先的汽车零配件制造商面临着如何利用最新的数字技术优化其制造车间管理的挑战。从设备主数据管理到生产执行工单管理,再到实时监控产量及能耗,需要一个灵活、快速且高…...

船舶机械设备5G智能工厂物联数字孪生平台,推进制造业数字化转型
船舶机械设备5G智能工厂物联数字孪生平台,推进制造业数字化转型。在当今数字化浪潮推动下,船舶制造业正经历着前所未有的变革。为了应对市场的快速变化,提升生产效率,降低成本,并增强国际竞争力,船舶机械设…...

什么是jsonp请求
JSONP(JSON with Padding)是一种解决跨域请求问题的技术。它允许网页从不同的域名请求数据,而不受同源策略的限制。JSONP 通过动态创建 script 标签来实现跨域请求,因为 script 标签不受同源策略的限制。 一、工作原理 客户端&a…...

【C++】STL容器详解【上】
目录 一、STL基本概念 二、STL的六大组件 三、string容器常用操作 3.1 string 容器的基本概念 3.2 string 容器常用操作 3.2.1 string 构造函数 3.2.2 string基本赋值操作 3.2.3 string存取字符操作 3.2.4 string拼接字符操作 3.2.5 string查找和替换 3.2.6 string比…...