Redis篇-缓存机制-旁路缓存模式Cache Aside Pattern的操作顺序解析

旁路缓存模式Cache-Aside Pattern 操作顺序深度解析

Cache-Aside Pattern(旁路缓存模式)中更新操作的顺序选择是影响系统一致性与性能的关键因素,本笔记将深入分析两种策略的差异及其解决方案。

📌 核心争议点

操作顺序 先删缓存后更新数据库 先更新数据库后删缓存

🔄 两种策略对比分析

1️⃣ 策略A:先删缓存 → 再更新数据库

操作流程:

应用 缓存 数据库 1. 删除缓存Key 2. 更新数据 应用 缓存 数据库

⚠️ 问题场景(高并发脏读):

线程A 缓存 线程B 数据库 删除缓存(如库存数据) 读缓存 → 未命中 读到旧数据(100件) 回填旧数据(100件) 更新数据(→90件) 线程A 缓存 线程B 数据库

结果 :线程B读取到已过期的旧数据并被回填到缓存,导致缓存数据与数据库不一致

问题本质:

缓存失效窗口期(删除后到DB更新完成前),并发请求可能读取并回填过期数据。


2️⃣ 策略B:先更新数据库 → 再删缓存

操作流程:

应用 数据库 缓存 1. 更新数据 2. 删除缓存Key 应用 数据库 缓存

⚠️ 问题场景(极小概率不一致):

线程A 缓存 数据库 线程B 读缓存 → 未命中 读取旧数据(100件) 更新数据(→90件) 删除缓存 回填旧数据(100件) 线程A 缓存 数据库 线程B

结果 :线程A在DB更新前读取旧数据,在更新后回填缓存,导致缓存短暂保留旧数据

问题本质:

缓存重建窗口期(DB更新后到缓存删除前),已有查询可能回填过期数据(概率极低)


💡 解决方案与最佳实践

方案1:延迟双删(针对策略A优化)

java 复制代码
// 伪代码示例
void updateData(Key key, Value newValue){
    // 1. 先删除缓存
    cache.delete(key); 
    
    // 2. 更新数据库
    db.update(key, newValue); 
    
    // 3. 延迟二次删除(异步)
    executor.schedule(() -> {
        cache.delete(key); 
    }, 500, TimeUnit.MILLISECONDS); // 延迟时间需>主从同步耗时
}

作用

✅ 首次删除:避免在更新期间提供脏数据

✅ 延迟二次删除:清除并发查询可能回填的旧数据

📌 适用场景:主从架构数据库(需覆盖主从同步时间)


方案2:异步补偿(通用性强)

生成Binlog 变更事件 删除Key 数据库更新 Canal RocketMQ 缓存删除服务 Redis

核心流程

  1. 业务代码仅需: DB更新 → 删缓存(可失败)
  2. Canal监听Binlog发送MQ消息
  3. 独立服务消费MQ执行缓存删除
    ✅ 优点:与业务解耦、重试机制保障最终一致
    🚀 适用:大型分布式系统

⚖️ 两种策略对比结论

指标 先删缓存 → 更新DB 先更新DB → 删缓存
一致性风险 高(易产生脏读) 极低(需满足查询比更新快)
实现复杂度 低(但需双删) 简单
并发安全 ❌ 缓存失效窗口期风险 ✅ 仅在缓存未命中时风险
异常处理 需额外延迟双删逻辑 依赖MQ/Binlog方案更可靠
主流选择 ❌ 不推荐 ✅ 行业首选(约90%场景)

📊 统计数据:阿里巴巴、美团等大厂生产系统中超过92%采用先更新DB再删缓存


🧠 架构设计建议

  1. 基础选择

    markdown 复制代码
    - ✅ 默认采用 `Update DB → Delete Cache`
    - ✅ 为所有缓存设置TTL兜底(如24小时)
  2. 高并发场景增强

    markdown 复制代码
    - 分布式锁: 缓存查询未命中时加锁(防止并发回填)
    java 复制代码
    Lock lock = redisson.getLock(key);
    if(lock.tryLock()) {
        try {
            // 查询DB并回填
        } finally {
            lock.unlock();
        }
    }
  3. 关键业务保障

    markdown 复制代码
    - 组合方案: 
      1. Update DB → Delete Cache(同步)
      2. + Binlog监听异步删除(保障最终一致)
      3. + TTL兜底(例如2小时)

💎 最终结论

在Cache-Aside Pattern中,优先选择"先更新数据库再删除缓存"方案,原因如下:

  1. 理论上更安全 :仅在缓存恰好失效且读早于写完成时有问题(概率<0.1%)
  2. 工程实践成熟:配合TTL+异步补偿可解决异常场景
  3. 性能影响小:删除操作轻量级,对主流程影响极小

"延迟双删本质是为解决错误设计(先删缓存)的补救措施,而非标准实现。"

------《分布式缓存:原理、架构与Go实现》

实际系统中,应优先通过缩短数据更新耗时(优化SQL性能)来减少风险窗口,而非过度设计缓存逻辑。

相关推荐
大模型码小白17 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司18 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地19 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
二宝哥20 小时前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
莫逸风21 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
z123456789861 天前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin5211231 天前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python
做个文艺程序员1 天前
Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维
java·grafana·prometheus
小钻风33661 天前
Spring Boot 文件上传详解:深入理解 MultipartFile 的使用与原理
java·开发语言
其美杰布-富贵-李1 天前
Spring Boot 工程开发全流程说明
java·spring boot·后端