数智应用帮
柔彩主题三 · 更轻盈的阅读体验

缓存策略优缺点:选对方法才能让系统又快又稳

发布时间:2025-12-09 18:28:28 阅读:330 次

缓存不是万能药,用错了反而拖后腿

系统开发时,一提到性能问题,很多人第一反应就是“加缓存”。确实,缓存能让重复请求的响应速度提升几倍甚至几十倍。比如用户频繁查看同一个商品详情页,如果每次都查数据库,不仅慢还容易把数据库压垮。但缓存本身也有代价,搞不好会引发数据不一致、内存爆炸等问题。

常见的缓存策略有几种:Cache-Aside、Read/Write Through、Write Behind 和 TTL 过期策略。每种都有适用场景,不能盲目照搬。

Cache-Aside:最常用但也最容易出错

这是目前最主流的做法——应用自己控制缓存的读写。读的时候先查缓存,没命中再查数据库,然后回填缓存;写的时候先更新数据库,再删掉缓存。

听起来挺顺,但有个经典问题:两个线程同时写数据,顺序乱了怎么办?比如线程A先把缓存删了,还没来得及更新数据库,线程B就读到了旧数据并重新加载进缓存,结果脏数据就留下来了。

解决方案通常是延迟双删,也就是更新数据库前后各删一次缓存,但这也不能百分百保证一致,只能降低概率。

// 伪代码示例:Cache-Aside 写操作
function updateProduct(product) {
deleteCache('product:' + product.id); // 先删缓存
updateDB(product); // 再更新数据库
deleteCache('product:' + product.id); // 再删一次,防并发
}

Read/Write Through:由缓存层接管一致性

这种模式下,应用只和缓存打交道,缓存服务自己负责同步到数据库。读请求直接走缓存,写请求也是先写缓存,由缓存去更新数据库。

好处是逻辑统一,应用不用操心数据同步细节。像 Redis + 某些持久化中间件就能实现类似机制。但问题在于,如果缓存层写数据库失败,整个写操作就得回滚,对缓存系统的稳定性要求更高。

而且一旦缓存服务挂了,所有读写都断,风险集中。

Write Behind:异步写入,性能强但复杂度高

这个策略在写操作时只更新缓存,然后异步批量把变更刷到数据库。典型场景是电商后台的商品浏览量统计,不需要实时落库,攒一批再写,减少数据库压力。

问题是数据可能丢失。比如还没来得及落库,缓存服务宕机了,那这部分数据就没了。所以它只适合容忍短暂丢失的场景,比如日志、计数类数据。

另外实现起来也麻烦,得有队列、重试、合并更新等机制支撑。

TTL 过期策略:简单粗暴但可能“卡顿”

给缓存设置一个过期时间,到期自动失效。比如新闻首页的热点榜单,每5分钟刷新一次,直接设 TTL=300 秒就行。

优点是实现简单,不用手动维护删除逻辑。但问题也很明显:过期那一刻如果大量请求涌入,全都会打到数据库上,造成“雪崩”。

为了避免这种情况,可以加个随机抖动,比如 TTL 设置为 300 ± 30 秒,让缓存分散失效。

// Redis 设置带随机偏移的过期时间
ttl = 300 + Math.random() * 60;
redis.setex('hot-news', ttl, data);

缓存穿透、击穿、雪崩:三大坑要避开

除了策略选择,实际运行中还有几个高频问题。

缓存穿透是指查一个根本不存在的数据,每次都不命中,请求直冲数据库。解决办法是用布隆过滤器提前拦截,或者对空结果也缓存几分钟。

缓存击穿是某个热点key过期瞬间被大量并发访问击中。可以用互斥锁,让第一个请求去加载数据,其他等着。

雪崩则是大面积缓存同时失效,整个系统瞬间被压垮。除了前面说的随机TTL,还可以做多级缓存,比如本地缓存+Redis组合,分摊压力。

缓存不是加了就万事大吉。什么时候该用哪种策略,得看业务对一致性、性能、可用性的权衡。金融交易系统宁可慢点也要数据准,可能更适合 Cache-Aside 加强校验;而内容推荐这类场景,稍微有点延迟没关系,Write Behind 反而更高效。