如何保证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查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

MySQL count()聚合函数详解

《MySQLcount()聚合函数详解》MySQL中的COUNT()函数,它是SQL中最常用的聚合函数之一,用于计算表中符合特定条件的行数,本文给大家介绍MySQLcount()聚合函数,感兴趣的朋... 目录核心功能语法形式重要特性与行为如何选择使用哪种形式?总结深入剖析一下 mysql 中的 COUNT

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

mysql中的服务器架构详解

《mysql中的服务器架构详解》:本文主要介绍mysql中的服务器架构,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、mysql服务器架构解释3、总结1、背景简单理解一下mysqphpl的服务器架构。2、mysjsql服务器架构解释mysql的架

MySQL之InnoDB存储引擎中的索引用法及说明

《MySQL之InnoDB存储引擎中的索引用法及说明》:本文主要介绍MySQL之InnoDB存储引擎中的索引用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录1、背景2、准备3、正篇【1】存储用户记录的数据页【2】存储目录项记录的数据页【3】聚簇索引【4】二

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

MySQL中的InnoDB单表访问过程

《MySQL中的InnoDB单表访问过程》:本文主要介绍MySQL中的InnoDB单表访问过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、访问类型【1】const【2】ref【3】ref_or_null【4】range【5】index【6】