如何保证Redis与Mysql双写一致性?

2024-09-02 14:12

本文主要是介绍如何保证Redis与Mysql双写一致性?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

https://www.cnblogs.com/coderacademy/p/18137480

延迟双删

对于上面链接的文章,里面的延迟双删没有给出具体的例子,也没有直接指出具体解决的问题是针对那种缓存策略,这里补充一下,延时双删缓存针对的是Cache aside pattern(缓存旁路策略),处理的是在高并发读写同时存在的情况下可能会出现的问题,详细如下。

什么是延迟双删:

延时双删策略能够有效解决缓存和数据库之间的数据不一致问题。它的核心思想是在更新数据库之后,先删除缓存中的数据,延迟一段时间后再次删除缓存中的数据。其具体步骤如下:

  1. 更新数据库:先将数据更新到数据库中。
  2. 删除缓存:立即删除缓存中对应的旧数据。
  3. 延迟一段时间:等待一段时间(通常是足够长以保证并发写入完成的时间)。
  4. 再次删除缓存:再次删除缓存中的数据,以防止并发操作在缓存中留下旧数据。

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双写一致性?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130179

相关文章

Mysql虚拟列的使用场景

《Mysql虚拟列的使用场景》MySQL虚拟列是一种在查询时动态生成的特殊列,它不占用存储空间,可以提高查询效率和数据处理便利性,本文给大家介绍Mysql虚拟列的相关知识,感兴趣的朋友一起看看吧... 目录1. 介绍mysql虚拟列1.1 定义和作用1.2 虚拟列与普通列的区别2. MySQL虚拟列的类型2

mysql数据库分区的使用

《mysql数据库分区的使用》MySQL分区技术通过将大表分割成多个较小片段,提高查询性能、管理效率和数据存储效率,本文就来介绍一下mysql数据库分区的使用,感兴趣的可以了解一下... 目录【一】分区的基本概念【1】物理存储与逻辑分割【2】查询性能提升【3】数据管理与维护【4】扩展性与并行处理【二】分区的

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

redis-cli命令行工具的使用小结

《redis-cli命令行工具的使用小结》redis-cli是Redis的命令行客户端,支持多种参数用于连接、操作和管理Redis数据库,本文给大家介绍redis-cli命令行工具的使用小结,感兴趣的... 目录基本连接参数基本连接方式连接远程服务器带密码连接操作与格式参数-r参数重复执行命令-i参数指定命

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危