2. 缓存与数据库交互基础:读流程(Cache Aside)、写流程(双写/延迟双删)、常见架构图

2.1 读流程:Cache Aside Pattern(旁路缓存)

Cache Aside 是业界最经典的缓存读取策略,核心原则是“由应用程序同时管理缓存和数据库,缓存不主动加载数据”。读流程分为缓存命中与缓存未命中两种情况。

读流程核心步骤(Cache Aside):
  1. 读请求到达:应用程序先查询缓存(Redis)。
  2. 缓存命中:直接返回缓存数据,流程结束。
  3. 缓存未命中:应用程序查询数据库(MySQL)。
  4. 回写缓存:将数据库查询结果写入 Redis,并设置过期时间。
  5. 返回数据:将数据返回给调用方。
读流程(Cache Aside)时序图 应用程序 Redis 缓存 MySQL 数据库 ① 查询缓存 ② 命中 → 返回数据 ③ 查询缓存 ④ 未命中(null) ⑤ 查询数据库 ⑥ 返回完整数据 ⑦ 写入缓存(设过期时间) ⑧ 返回数据给调用方 💡 关键原则:缓存由应用程序主动写入,数据库是数据权威源。缓存未命中时才回写,避免缓存与数据库同时写入的竞争。

2.2 写流程:双写策略与延迟双删

写流程的核心挑战是“如何保证缓存与数据库的数据最终一致”。常见的写策略包括双写和延迟双删,后者是前者的增强版本。

2.2.1 双写策略(先更新数据库,再更新缓存)

步骤 操作 说明
1 更新数据库 执行 UPDATE / INSERT 语句,数据库写入成功
2 更新缓存 将新数据直接写入 Redis(SET 操作)
⚠️ 双写策略的风险:
  • 并发写冲突:线程 A 先更新数据库(值=100),线程 B 后更新数据库(值=200),但线程 B 先更新缓存(值=200),线程 A 后更新缓存(值=100)→ 缓存中残留旧值(100)。
  • 适用场景:低并发、对短暂不一致容忍度较高的系统。

2.2.2 延迟双删策略(推荐方案)

延迟双删在“先更新数据库,再删除缓存”的基础上,增加一次“延迟删除”操作,以解决并发读写导致的不一致问题。

延迟双删执行步骤:
  1. 删除缓存:先删除 Redis 中的旧缓存。
  2. 更新数据库:执行数据库写入操作。
  3. 延迟删除:休眠一段时间(通常 100~500ms),再次删除缓存。
写流程:延迟双删策略时序图 应用程序 Redis 缓存 MySQL 数据库 ① 删除缓存(DEL) ② 更新数据库(UPDATE) ③ 数据库写入成功 ④ 延迟 100~500ms ⑤ 再次删除缓存(DEL) 💡 延迟双删为什么有效? • 第一次删除:清除旧缓存,避免读请求读到脏数据。 • 延迟窗口:等待可能发生的并发读请求将旧数据回写到缓存。 • 第二次删除:清除并发读回写的旧数据,保证最终一致性。 • 延迟时间建议:大于一次数据库查询 + 一次缓存回写的平均耗时(通常 100~500ms)。

2.3 常见架构图:缓存与数据库交互拓扑

在实际生产环境中,Redis 缓存与 MySQL 数据库的交互通常采用分层架构,配合负载均衡、哨兵/集群等高可用组件。以下为典型的三层读写分离架构。

缓存与数据库交互架构图(读写分离 + 延迟双删) 客户端 / 应用层 Redis 缓存层(哨兵 / 集群) 读请求优先 → 缓存命中直接返回 MySQL 数据库层(主从复制) 写请求 → 主库写入;读请求 → 从库读取 写流程(延迟双删) 读流程(Cache Aside) 💡 架构要点: • 读请求优先走 Redis,缓存未命中时回源到 MySQL 从库,回写缓存。 • 写请求先删除缓存,再更新 MySQL 主库,延迟后再次删除缓存(延迟双删)。

2.4 三种写策略对比

策略 核心操作 一致性保证 并发风险 推荐场景
先更新缓存,再更新数据库 SET → UPDATE 低(极易不一致) 极高 不推荐
先更新数据库,再更新缓存(双写) UPDATE → SET 中(并发写可能覆盖) 高 低并发、可接受短暂不一致
先更新数据库,再删除缓存(延迟双删) DEL → UPDATE → 延迟 → DEL 高(最终一致性) 低 生产环境推荐
📌 测试验证要点:
  • 延迟双删的“延迟时间”需要根据业务查询耗时动态调整,建议通过压测确定最优值。
  • 第二次删除失败时,需要引入重试机制(如消息队列 + 异步重试),否则缓存可能永久不一致。
  • 读流程中,缓存回写操作必须设置合理的过期时间(TTL),作为最终一致性的兜底手段。