当前位置: 首页 > web >正文

详解Redis数据库和缓存不一致的情况及解决方案

数据库与缓存不一致是分布式系统中常见问题,本质是数据在缓存层和存储层出现版本差异

一、并发写操作导致不一致(最常见)

  • 场景描述

    • 线程A更新数据库 → 线程B更新数据库 → 线程B更新缓存 → 线程A更新缓存

    • 结果:缓存中存储的是线程A的旧数据

  • 发生条件

  • 解决方案

    • 分布式锁:对同Key的写操作加锁

    • 串行化队列:将写请求放入MQ顺序执行

二、先更新数据库后删除缓存失败(Cache-Aside模式)

  • 场景描述

    • 更新数据库成功

    • 删除缓存失败(网络抖动/Redis超时)

    • 结果:缓存中残留旧数据

  • 关键代码风险点

    public void updateData(Data data) {db.update(data);          // 步骤1:数据库更新成功cache.delete(data.getId()); // 步骤2:缓存删除失败!
    }
  • 解决方案

    • 重试机制

      void deleteWithRetry(String key, int maxRetry) {int retry = 0;while (!cache.delete(key) && retry++ < maxRetry) {Thread.sleep(50);}
      }
    • 异步补偿:通过Binlog监听(如Canal)触发二次删除

三、主从延迟导致脏读(读写分离架构)

  • 场景描述

    • 主库更新成功 → 删除缓存

    • 读请求从未同步的从库读取旧值 → 回填缓存

    • 结果:缓存被旧数据污染

  • 解决方案

    • 延迟双删

      db.update(data);          // 更新主库
      cache.delete(key);        // 首次删除
      Thread.sleep(500);       // 等待主从同步
      cache.delete(key);        // 二次删除
    • 强制读主库:对一致性要求高的查询直连主库

四、缓存过期时高并发读(Cache Miss风暴)

  • 场景描述

    • 缓存过期瞬间涌入大量读请求

    • 请求1查DB → 请求2查DB → ... → 请求N查DB

    • 多个线程同时回填缓存(可能乱序)

    • 结果:缓存可能被中间状态数据覆盖

  • 极端案例

    • 请求1读取到旧值V1,回填耗时久

    • 请求2读取新值V2并先完成回填

    • 请求1最终将V1写入缓存(覆盖V2)

  • 解决方案

    • 互斥锁重建:仅允许一个线程重建缓存

    • 逻辑过期:物理缓存永不过期,通过逻辑时间控制

五、批量操作与部分失效

  • 场景描述

    • 场景1:批量更新数据库成功,但部分缓存删除失败

    • 场景2:分页查询缓存无法感知单条数据变更

    • 结果:缓存中存在部分脏数据

  • ​​​​​​​典型案例

    • 商品列表页缓存无法感知单个商品价格变更

    • 订单列表缓存未失效时,订单状态已更新

  • 解决方案

    • 缓存维度拆分:按查询条件设计缓存Key

    • 增量广播:通过消息队列通知相关缓存失效

    • 短过期时间:对聚合查询设置更短TTL

六、跨服务数据变更

  • 场景描述

    • 服务A更新数据库 → 服务B的缓存未失效

    • 原因:服务间缺乏缓存协同机制

    • 结果跨服务缓存残留旧数据

  • ​​​​​​​微服务常见问题

  • 解决方案

    • 领域事件通知:通过消息队列(Kafka/RabbitMQ)广播变更

    • 统一缓存层:所有服务通过SDK操作缓存,SDK统一处理失效

七、终极解决之道:取舍策略

        根据业务需求选择合适的一致性级别:

策略一致性强度性能影响适用场景
先删缓存再更新DB写少读多
先更新DB再删缓存通用方案(需重试)
双写+事务金融交易
延迟监听(Binlog)最终一致高并发写场景
忽略不一致+短TTL允许短暂脏读的业务

         重要原则:不要尝试绝对强一致,除非接受性能断崖式下降。通常建议采用 "更新DB + 延迟双删 + 重试队列" 的组合策略。

http://www.xdnf.cn/news/14693.html

相关文章:

  • WEB3合约开发以太坊中货币单位科普
  • react day.js使用及经典场景
  • 【代码解析】opencv 安卓 SDK sample - 1 - HDR image
  • SQL 分页方法全解析:从基础到高级应用
  • 深入解析ID3算法:信息熵驱动的决策树构建基石
  • 【Qt开发】网络运用
  • 项目中后端如何处理异常?
  • JAVA锁机制:对象锁与类锁
  • 一、什么是生成式人工智能
  • GPT-1 与 BERT 架构
  • MySQL之InnoDB存储引擎深度解析
  • 软件工程期末试卷填空题版带答案(共40道)
  • 【环境配置】在Ubuntu Server上安装5090 PyTorch环境
  • CVE-2024-6387漏洞、CVE-2025-26465漏洞、CVE-2025-26466漏洞 一口气全解决
  • 题解:P11501 [ROIR 2019] 探险队(Day 2)
  • 【软考高级系统架构论文】论无服务器架构及其应用
  • 在 `setup` 函数中使用 Vuex
  • 通过 Lambda + API Gateway + 外部 API 实现。
  • Django数据库迁移
  • LLM:重构数字世界的“智能操作系统”
  • Java面试题025:一文深入了解数据库Redis(1)
  • Docker高级管理--容器通信技术与数据持久化
  • 【ubuntu下小工具】Crontab定时任务进行数据备份和清理
  • 【AGI】突破感知-决策边界:VLA-具身智能2.0
  • 格兰泰勒棱镜透射光强曲线优化处理
  • 嵌入式开发之嵌入式系统架构如何搭建?
  • Java ArrayList集合和HashSet集合详解
  • day38 打卡
  • 基于Python、tkinter、sqlite3 和matplotlib的校园书店管理系统
  • 多线程八股