缓存的应用场景与读写模式
聊到缓存,很多人第一反应就是“快”。但缓存这东西,不是银弹。什么时候该用,什么时候不该用,用错了比不用还坑。我见过不少团队,上来就把所有数据往Redis里塞,结果内存爆了,业务反而更慢了。
我个人习惯是:先想清楚场景,再动手写代码。一个原则——数据热不热?热点数据才值得缓存。冷数据,你放进去就是浪费内存。
什么时候适合用缓存?
逻辑其实很直接。数据读得多、写得少,这种场景天然适合缓存。比如配置信息、用户头像URL、热门文章列表。
- 读远多于写:比例在几十比一以上。典型的就是首页热点内容。我曾经接手过一个项目,后台配置几乎不变,但每次请求都去查数据库。加了一层本地缓存后,接口耗时从200ms降到了2ms。
- 可容忍短暂不一致:缓存是最终一致的。如果你的业务要求强一致,比如银行转账,那最好别碰缓存,或者用很短的过期时间。
- 计算开销大:比如复杂的SQL查询,或者需要调用多个外部服务聚合数据。我把它这种场景称为“慢到无法忍受的结果”,缓下来是真的香。
什么时候不适合?
反过来,数据写特别频繁,或者时刻变化的,缓存的意义就不大。你想想看,刚写进去就要失效,缓存一直在刷,白白浪费CPU和内存。
- 高频写、低频读:比如用户频繁修改文档,但很少有人去看。
- 强一致性要求:金融交易、库存扣减,这些场景必须实时落库。
读写模式:你总得选一种
说完了场景,我们再聊聊模式。这是我个人比较看重的部分,因为模式选错了,后面维护成本翻倍。
读模式
读模式相对简单,就是“先看缓存,再看数据库”的套路。但这里有一个经典问题——缓存穿透。
| 模式 | 核心步骤 | 注意点 |
|---|---|---|
| Cache-Aside (旁路缓存) | 应用先查缓存,命中直接返回;未命中则查数据库,写入缓存再返回。 | 这是开发中最常见、最容易理解的模式。大多数项目第一次上缓存都用这个。 |
| Read-Through (穿透读) | 应用只跟缓存交互,缓存负责去数据库查询并填充自己。 | 应用层更简洁,但缓存层要承担更多逻辑。依赖缓存中间件的支持。 |
避坑案例:我曾经遇到一个项目,大量请求查一个不存在的数据。缓存里没有,数据库也没有,每次请求都穿透到数据库。结果数据库被打挂了。解决这个问题,我们后来加了一层“空值缓存”。
// 伪代码示例:Cache-Aside + 空值缓存
Object value = cache.get("key");
if (value == null) {
// 数据库查询
value = database.find("key");
if (value == null) {
// 设置一个空值缓存,过期时间短一些
cache.set("key", "EMPTY", 60);
} else {
cache.set("key", value, 3600);
}
} else if ("EMPTY".equals(value)) {
// 直接返回空,不去查数据库
return null;
}
return value;
写模式
写模式就复杂多了。说白了,就是要不要保证数据库和缓存实时一致。我告诉你,大部分业务根本不需要。
| 模式 | 做法 | 典型场景 |
|---|---|---|
| Write-Through (穿透写) | 更新缓存的同时,同步更新数据库,要保证事务性。 | 关键数据写入,比如用户积分变更。 |
| Write-Behind (异步写) | 先更新缓存,再异步把数据写回数据库,合并写操作。 | 写频率高、数据精度要求不高的场景,如访问计数、点赞数。 |
| Write-Around (绕开写) | 直接更新数据库,同时删除缓存(或让缓存自然过期)。 | 读多写少的配置信息。我比较喜欢这种模式,简单干净,不容易出并发问题。 |
为什么我说大部分业务可以接受不一致?因为很多场景下,读操作并不要求最新。比如一个新闻列表,一秒钟前更新和一分钟前更新,对用户来说没区别。这时候用Write-Around就足够。
特别注意:在写模式中,并发场景下“先删缓存再更新数据库”容易出现脏数据。正确的顺序是“先更新数据库,再删缓存”——也就是“Cache Invalidation”策略。我亲眼见过一个线上事故,就是因为顺序写反,导致缓存里长期存着旧数据,用户看到的永远不是最新的。
一张图讲清场景与模式
我把上面的内容理了下,画了一张结构图。你看看就知道,场景决定模式,模式决定代码实现。
你从这张图能看到一个比较清晰的链路:数据访问频率决定了你是读密集型还是写密集型。读密集型,通常就是用旁路缓存或穿透读;写密集型,要考虑同步写还是异步写。最后,不管哪种模式,最终都要落到缓存失效与一致性上。
核心原则总结:没有最好的缓存策略,只有最适合的。读多写少用 Cache-Aside,写多读少用 Write-Behind,强一致性场景慎用缓存。脑子里的模型越清晰,写出来的代码就越稳。