Redis 缓存怎么更新?—— 四种模型与一次“迟到的删除”

一、为什么"更新缓存"值得单独聊

缓存位于内存,数据库位于磁盘,两者天生有时差;再加上并发请求可能交错读写,如果不设计好更新路径,就会出现"缓存里一直是老数据"的现象。下面按"一致性强度"从高到低,梳理四种常见模型,并补充一个工程上常用的"迟到删除"技巧。


二、Write-Through(读写穿)------ 缓存代理一切

流程

  • 读 miss:缓存自己去数据库加载,再返回

  • 写:缓存同步写数据库,两边都成功才返回客户端

特点

  • 业务代码最简洁;缓存与数据库强一致

  • 写延迟 = 数据库延迟;缓存层要实现 Loader/Writer 接口

  • 适合对一致性要求极高、写量可控的系统,如计费、银行核心账务


三、Write-Behind(写回)------ 先写内存,再异步刷盘

流程

  • 写请求只到达缓存立即返回

  • 缓存按"时间窗口"或"队列长度"批量回写数据库

特点

  • 写操作极快,可轻松抗住十万级 TPS

  • 数据库压力被批量平滑

  • 宕机窗口内可能丢失数据;读也可能短暂读到脏数据

  • 适用场景:计数器、日志、秒杀库存(允许少量超卖后补偿)


四、Cache-Aside(旁路缓存)------ 业务自己掌握读写

流程

  • 读:缓存 miss → 查数据库 → 把结果写回缓存

  • 写:先更新数据库 → 然后删除缓存(注意是删除,而不是修改)

为什么写后选择"删除"

  1. 缓存可能是聚合对象(JSON/Hash),局部字段更新成本高

  2. 删除是幂等操作,无需考虑并发写顺序

  3. 下一次读请求会重新加载最新数据,天然保持最终一致

不足

数据库提交与缓存删除之间仍有微小时间窗,极端并发下可能把旧值重新载入缓存。要缩小这个窗口,就出现了"延迟双删"。


五、延迟双删------把窗口再压短一点

思路

  1. 立即删除:挡住大多数并发读

  2. 延迟一段时间(通常 0.5--2 秒)后再删除一次:把"刚被回填的旧值"清掉

实现示例

java 复制代码
@Transactional
public void updateUser(User u) {
    userDao.update(u);                 // 1. 写库
    redis.del("user:" + u.getId());    // 2. 立即删
    // 3. 延迟删(线程池、MQ、Redisson 延迟队列均可)
    scheduler.schedule(() -> redis.del("user:" + u.getId()), 1, TimeUnit.SECONDS);
}

关键参数

  • 延迟时长 ≈ 主从复制延迟 + 业务 RT 的 99 分位

  • 延迟任务需保证"至少一次"投递,失败要有重试或监控

延迟双删并未突破 CAP,只是把"最终一致"的"最终"两字压到亚秒级,成本低,落地简单。


六、MQ 双写------用消息流解耦缓存

流程

  1. 业务只负责写数据库并提交事务

  2. 事务提交后发送 binlog 或自定义消息

  3. 下游消费者异步重建缓存

特点

  • 写链路零侵入,缓存与业务彻底解耦

  • 可削峰、可重试、可批量聚合

  • 秒级延迟;需要保证消息不丢、不乱序

  • 适合订单、物流等读远大于写,且能接受秒级滞后的系统


七、如何选型

  1. 强一致 + 写量可控 → Write-Through

  2. 写极高并发 + 可丢少量数据 → Write-Behind

  3. 读多写少 + 亚秒级可接受 → Cache-Aside,配合延迟双删

  4. 读多写少 + 可接受秒级延迟 → MQ 双写

先按"一致性"要求划定范围,再根据"写入吞吐量"微调,就能落到唯一象限。


八、结语

缓存更新没有万能答案,只有在"一致性---性能---复杂度"三角形里最适合自己的点。

理解每种模型的前提与代价,才能在真实场景里做出可靠的选择。祝你落地顺利,少踩坑。

相关推荐
星释2 小时前
Rust 练习册 :掌握文本处理与词频统计
开发语言·后端·rust
方圆想当图灵2 小时前
Cursor 无法跨项目读取源码怎么办?MCP Easy Code Reader 帮你解决!
后端·cursor·mcp
TDengine (老段)2 小时前
从细胞工厂到智能制造:Extracellular 用 TDengine 打通数据生命线
java·大数据·数据库·科技·制造·时序数据库·tdengine
Boop_wu2 小时前
[Java EE] 多线程 -- 初阶(1)
java·jvm·算法
JaguarJack2 小时前
深入理解 Laravel Middleware:完整指南
后端·php·laravel
绝无仅有2 小时前
某多多大厂面试相关计算机网络知识点总结
后端·面试·架构
风象南2 小时前
SpringBoot实现隐式参数注入
后端
绝无仅有2 小时前
调用服务出现网络错误的问题排查与解决
后端·面试·架构