在典型的互联网架构中,MySQL 承担着数据持久化的重任,保证数据的可靠性;而 Redis 作为高速缓存,大幅提升数据访问性能。但二者并用时,如何保证MySQL与Redis缓存一致性(即缓存一致性问题)成为经典难题。

本文深入剖析缓存不一致产生的根本原因,并详细对比四种主流的缓存更新设计模式,帮助你根据业务场景做出合理取舍。

一、缓存不一致是如何产生的?

如果数据从不变更,缓存与数据库自然永远一致。但实际业务中,数据更新频繁,每次更新都要同时操作数据库和缓存——两个独立的系统,无法保证原子性。在并发读写场景下,就会出现短暂的数据不一致。理论上分布式事务可以做到强一致,但实现成本过高,很少在生产中采用。

因此,业界公认的思路是:不追求强一致,而是保证最终一致,并通过合理的设计将不一致的影响时间和范围降到最低。

二、四种缓存更新设计模式详解

以下是经典的四种缓存更新策略,各自适用不同场景。我们将逐一剖析其流程、优缺点及并发下的表现。

1. 先删除缓存,再更新数据库(不可取)

流程:删除缓存 → 更新数据库。这种方案在并发读写下极易产生长时间的脏数据。先删除缓存再更新数据库的时序图

图:先删除缓存再更新数据库(并发场景导致脏数据)

并发问题示例: 线程A删除缓存后尚未更新数据库,线程B读取缓存未命中,从数据库读到旧数据并写入缓存,然后线程A才更新数据库。最终缓存中是旧数据,数据库是新数据,产生不一致。

结论: 一般不建议使用这种方式。

2. 先更新数据库,再删除缓存(Cache Aside Pattern)

流程:更新数据库 → 删除缓存。这是最常用的模式,被诸多大厂实践验证。


图:先更新数据库再删除缓存

并发表现: 极小概率出现短暂不一致(例如线程A更新数据库后、删除缓存前,线程B读到旧缓存),但很快缓存被删除,后续请求会读数据库并重建缓存,最终一致。大部分业务可接受此短暂窗口。

优点: 实现简单,脏数据风险低。
适用场景: 读多写少的通用业务。

3. 只更新缓存,由缓存同步更新数据库(Read/Write Through Pattern)

业务代码只操作缓存,缓存层负责同步写入数据库。读操作若缓存未命中,缓存也同步从数据库加载。

图:Write Through 模式

优点: 缓存不一致概率极低,对业务层透明。
缺点: 需要对缓存中间件进行深度改造(如自定义Redis代理),实现成本高。

🔧 适用场景: 对一致性要求极高,且有自研缓存中间件能力的团队。

4. 只更新缓存,由缓存异步更新数据库(Write Behind Cache Pattern)

业务只更新缓存,立即返回;缓存以异步方式(如消息队列、后台线程)批量或延时写入数据库。

图:Write Behind 异步写模式

优点: 读写性能极高,操作内存即返回,适合超高吞吐场景。
缺点: 非强一致,缓存宕机会导致数据丢失;实现复杂,需要处理异步重试和幂等。

⚠️ 适用场景: 对性能要求极致,可容忍少量数据丢失(如点赞数、浏览计数)。


三、方案对比与选型建议

模式一致性保障性能影响实现复杂度推荐指数
先删缓存后更新DB低(极易脏数据)简单★☆☆☆☆
Cache Aside中(最终一致)高(删除缓存轻量)简单★★★★★
Read/Write Through高(同步写入)中(同步写入DB有开销)★★★☆☆
Write Behind低(可能丢数据)极高(纯内存操作)★★☆☆☆

综合建议: 绝大多数业务场景下,Cache Aside Pattern(先更新数据库,再删除缓存) 是最佳选择。它兼顾了简单性和一致性,也是 京东云Redis 和 云数据库RDS 官方推荐的使用模式。

四、总结

没有完美的缓存一致性方案,只有最适合当前业务的方案。在设计系统时,请牢记:

  • 如果不能容忍任何不一致,请直接读数据库,不要使用缓存。
  • 如果可以容忍秒级延迟,Cache Aside 完全够用。
  • 如果写吞吐量极大且允许少量丢失,可考虑 Write Behind。
  • 任何缓存更新逻辑,都要配合缓存过期时间作为兜底,避免永远脏数据。

希望本文能帮助你更好地处理 MySQL 与 Redis 的缓存一致性问题。