Redis和数据库双写一致性问题的解决方案

 更新时间:2025年08月06日 08:48:41   作者:Java自学之旅  
文章探讨了Redis与数据库双写一致性问题,提出四种解决方案:先更新数据库再删除缓存(推荐),延时双删避免并发不一致,监听数据库变更实现最终一致性,加分布式锁保障强一致,核心原则是优先保证数据库正确性,缓存操作可失败重试,强一致性需牺牲性能

下面我将详细解答Redis与数据库双写一致性问题,结合故事举例说明,最后用思维导图总结核心解决方案。

问题解答

核心问题:当同时更新Redis缓存和数据库时,由于网络延迟、操作顺序等问题导致数据不一致。

解决方案(含故事举例)

假设电商平台有商品库存系统:

// 商品服务类
public class ProductService {
    private DB db = new MySQL();  // 数据库
    private Cache cache = new Redis();  // 缓存
}

1. 先更新数据库,再删除缓存(推荐方案)

public void updateProduct(Product product) {
    db.update(product);          // 1. 更新数据库
    cache.delete(product.getId());// 2. 删除缓存
}

故事场景

管理员修改商品价格(100→80元):

  • 数据库先更新为80元 ✅
  • 删除Redis中该商品的缓存

用户查询时:

  • 缓存未命中 → 读数据库(80元) ✅
  • 回填缓存(80元) ✅

优势

  • 避免"更新缓存失败导致永久不一致"
  • 缓存删除失败可重试(通过消息队列)

风险

并发时短暂不一致(概率低):

  • 用户A读缓存(空) → 读数据库(100元)
  • 管理员更新数据库(100→80) → 删缓存
  • 用户A写缓存(100元) ❌(旧数据)
  • 解决方案:延时双删(下文说明)

2. 延时双删策略

public void updateWithDelay(Product product) {
    cache.delete(product.getId());    // 1. 先删缓存
    db.update(product);               // 2. 更新数据库
    Thread.sleep(500);                // 3. 等待500ms
    cache.delete(product.getId());    // 4. 再删缓存
}

故事场景

解决上述并发问题:

  • 首次删除:清空旧缓存
  • 更新数据库 ✅
  • 等待期间可能写入的旧缓存被二次删除 ❌→✅

3. 监听数据库变更(最终一致性)

// 使用Canal监听MySQL binlog
canal.subscribe(event -> {
    if (event.isUpdate()) {
        cache.delete(event.getKey()); // 异步删除缓存
    }
});

故事场景

订单系统库存变更:

  • 数据库减库存 ✅
  • Canal捕获变更事件
  • 自动删除Redis库存缓存
  • 下次查询回填最新值 ✅

4. 加分布式锁(强一致)

public void safeUpdate(Product product) {
    Lock lock = redisson.getLock("PRODUCT_" + product.getId());
    lock.lock();
    try {
        db.update(product);
        cache.update(product); // 同时更新缓存
    } finally {
        lock.unlock();
    }
}

适用场景

金融账户余额等强一致性要求:

  • 读写操作串行化
  • 性能较低(非高并发场景)

总结对比

方案一致性性能复杂度适用场景
先DB后删缓存最终★★★★大部分业务
延时双删最终★★★高并发场景
监听binlog最终★★★★异构系统同步
分布式锁强一致★★金融/账户系统

核心原则

  • 优先保证数据库正确性
  • 缓存操作可失败/重试
  • 强一致性需牺牲性能

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • redis用list做消息队列的实现示例

    redis用list做消息队列的实现示例

    本文主要介绍了redis用list做消息队列的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Redis 缓存满了如何解决

    Redis 缓存满了如何解决

    Redis 缓存使用内存来保存数据,随着需要缓存的数据量越来越大,有限的缓存空间不可避免地会被写满,本文主要介绍了Redis 缓存满了如何解决,感兴趣的可以了解一下
    2023-08-08
  • 如何高效地向Redis插入大量的数据(推荐)

    如何高效地向Redis插入大量的数据(推荐)

    本篇文章主要介绍了如何高效地向Redis插入大量的数据,现在分享给大家,感兴趣的小伙伴们可以参考一下。
    2016-11-11
  • 解析Redis未授权访问漏洞复现与利用危害

    解析Redis未授权访问漏洞复现与利用危害

    这篇文章主要介绍了Redis未授权访问漏洞复现与利用,介绍了redis未授权访问漏洞的基本概念及漏洞的危害,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-01-01
  • Redis实现优惠券限一单限制详解

    Redis实现优惠券限一单限制详解

    这篇文章主要介绍了Redis解决优惠券秒杀应用案例,本文先讲了抢购问题,指出其中会出现的多线程问题,提出解决方案采用悲观锁和乐观锁两种方式进行实现,然后发现在抢购过程中容易出现一人多单现象,需要的朋友可以参考下
    2022-12-12
  • 在K8s上部署Redis集群的方法步骤

    在K8s上部署Redis集群的方法步骤

    这篇文章主要介绍了在K8s上部署Redis集群的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 为啥Redis使用pipelining会更快

    为啥Redis使用pipelining会更快

    这篇文章主要介绍了为啥Redis使用pipelining会更快,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Redis主从复制分步讲解使用

    Redis主从复制分步讲解使用

    Redis因为其高性能和易用性在我们后端的服务中发挥了巨大的作用,并且很多重要功能的实现都会依赖redis,本篇我们来了解Redis高可用主从复制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-09-09
  • Redis Scan命令的基本使用方法

    Redis Scan命令的基本使用方法

    这篇文章主要给大家介绍了关于Redis中Scan命令的基本使用方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-12-12
  • Redis集群增加节点与删除节点的方法详解

    Redis集群增加节点与删除节点的方法详解

    这篇文章主要给大家介绍了关于Redis集群增加节点与删除节点的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09

最新评论