拓冰建站拓冰建站
首页 / 资讯中心 / 正文

缓存更新策略深度解析:从Cache Aside到延迟双删的实践指南

1. 缓存更新策略黑马点评里最容易被问倒的一块黑马点评这个项目很多人做完之后感觉良好代码能跑、接口能通、缓存也加上了但一到面试被问到“你的缓存是怎么更新的一致性问题怎么解决”就卡壳。原因很简单这个项目里缓存更新策略的代码量并不大但背后的门道非常多属于典型的“代码一行、原理一车”的知识点。我见过太多简历上写着“熟练使用Redis缓存优化接口性能”的候选人一追问缓存和数据库的一致性怎么保证回答基本停在“删缓存就行了”这个层面。但你真让他讲讲为什么是删而不是更新、为什么先操作数据库再删缓存、并发下会不会有问题就开始支支吾吾了。这篇内容我按“黑马点评技术汇总”系列的节奏把缓存更新策略这块彻底拆开揉碎。不光是项目里那些代码怎么写的更重要的是写这些代码背后的依据是什么、面试官到底想听到什么以及你实际做项目时会踩到哪些坑。内容适合正在学黑马点评的人复盘也适合准备面试的人做知识点梳理。2. 缓存更新策略的核心框架先搞清楚有哪些模式2.1 四种主流缓存更新模式对比缓存更新策略本质上解决的是一个老问题数据同时存在于数据库和缓存中当数据库里的数据发生变化时缓存怎么办。有些方案选择更新缓存有些方案选择删除缓存有些方案不主动更新而是等缓存过期每种方案都有自己的适用场景。业内比较经典的四种模式先列出来再逐个分析模式核心思路一致性水平复杂度适用场景Cache Aside读缓存miss则查库回填更新时先操作DB再删缓存较高低绝大多数业务黑马点评用的就是这种Read Through缓存组件自己负责从DB加载数据业务只跟缓存交互高中有统一缓存中间件的情况Write Through写数据时同步写DB和缓存事务内完成最高中高对一致性要求极高、写并发不高Write Behind先写缓存异步批量回写DB可能存在丢数据高写多读少、能容忍数据丢失黑马点评项目里使用的是Cache Aside模式这是当前互联网业务中最主流、也最适合学习的方案。原因很实在它足够简单业务代码对缓存的操作完全可控一致性也能通过一些技巧提升到业务可接受的水平。面试时你把Cache Aside讲透了基本就能覆盖大多数问题。2.2 为什么黑马点评选择Cache Aside而不是其他模式先说结论黑马点评作为教学项目选择Cache Aside不是因为它技术最先进而是因为它最贴近真实业务场景、最容易理解也最能展示面试官想听的“为什么”。具体原因有三层。第一层黑马点评的缓存场景是典型的读多写少。商户查询、店铺类型列表、博客详情这些数据的特征是读的频率远高于写。Cache Aside模式在读取路径上把缓存命中率做高更新路径上通过删除缓存来让下一次读取重新加载这种“读多写少”场景里性价比最高。第二层Write Through和Write Behind虽然一致性更好或者写性能更强但都需要引入额外的中间件或框架支持。黑马点评是个单体项目用Spring Data Redis直接操作缓存引入一套完整的缓存框架反而掩盖了底层原理的学习价值。第三层也是最关键的一点Cache Aside模式下更新策略的处理逻辑全部由开发人员自己掌控。先更新数据库还是先删缓存、删除失败怎么办、要不要延迟双删这些问题的答案都需要你自己思考。面试官问缓存更新策略想听的正是你的思考过程。3. 黑马点评中的缓存更新实现细节3.1 核心操作查询走缓存更新删缓存黑马点评里关于缓存的核心逻辑基本可以总结成两句话查询时先查缓存缓存没命中再查数据库并回填缓存更新时先更新数据库再删除缓存不直接更新缓存。这个“删除缓存而不是更新缓存”的决策是很多初学者第一个不理解的地方。为什么数据库更新了缓存不跟着更新反而要删掉让下次查询重新加载答案在于“更新缓存”的成本和风险都比“删除缓存”高得多。更新的成本高是因为更新操作可能涉及复杂的计算。比如一个商户的评分可能是多种维度加权算出来的如果每次数据库字段变化都同步计算一遍缓存值写路径会变得很重而且可能频繁触发无意义的计算。更新的风险高是因为并发环境下“更新缓存”很容易产生旧值覆盖新值的问题。两个线程同时写数据库线程A先改了一个值线程B后改了一个值但线程B的网络延迟导致它的缓存更新请求先到达Redis这时候缓存里存的是旧值和数据库不一致了。删除缓存的容错性就好很多不管哪个线程删下次读的时候从数据库拿到的都是最新值。3.2 商户查询缓存的具体落地代码黑马点评里最典型的缓存更新场景是商户查询。我简单贴一下核心逻辑的伪代码帮助回忆整个流程public Result queryById(Long id) { // 1. 查缓存 String shopJson stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY id); if (StrUtil.isNotBlank(shopJson)) { Shop shop JSONUtil.toBean(shopJson, Shop.class); return Result.ok(shop); } // 2. 缓存未命中查数据库 Shop shop getById(id); if (shop null) { return Result.fail(店铺不存在); } // 3. 回填缓存 stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY id, JSONUtil.toJsonStr(shop)); return Result.ok(shop); } public Result update(Shop shop) { Long id shop.getId(); if (id null) { return Result.fail(店铺id不能为空); } // 1. 更新数据库 updateById(shop); // 2. 删除缓存 stringRedisTemplate.delete(CACHE_SHOP_KEY id); return Result.ok(); }这段代码看起来简单但每一个选择背后都有原因。先看查询路径先查Redis命中直接返回JSON并转成对象没命中就查数据库查到之后回填缓存。这个流程对应的是“缓存未命中”的处理也是Cache Aside的读路径标准实现。再看更新路径先调用updateById(shop)更新数据库然后delete删除缓存。这里有个顺序问题为什么是先更新数据库再删缓存而不是反过来这个问题放到后面专门讲因为它是缓存更新策略里的核心考点。3.3 缓存过期时间给缓存加上生存周期黑马点评的缓存里最容易被忽略但又特别重要的一个细节是回填缓存的时候设置过期时间。stringRedisTemplate.opsForValue().set( CACHE_SHOP_KEY id, JSONUtil.toJsonStr(shop), 30L, TimeUnit.MINUTES );设置过期时间的意义在于解决“缓存和数据库长期不一致”的问题。哪怕删缓存失败、哪怕并发下出现了短暂的脏读只要缓存有过期时间最坏情况下30分钟之后缓存自动失效数据会从数据库重新加载一致性最终会收敛。这也是缓存更新策略里很重要的一个概念最终一致性。业务场景对实时性要求没那么高的时候不一定要做到强一致只要保证一段时间内能收敛到一致即可。从实际操作来看我这里有个建议过期时间不要设成一个固定值。黑马点评里设定30分钟没问题但真实业务里如果大量缓存的过期时间都一样很容易出现缓存雪崩。更稳妥的做法是加一个随机值比如30分钟加0到5分钟的随机数把过期时间打散。4. 先更新数据库还是先删缓存并发视角的深度分析4.1 两种顺序各自的隐患推演缓存更新策略里最经典的问题就是先更新数据库再删缓存还是先删缓存再更新数据库背答案很容易但理解了为什么面试才不会慌。先看“先删缓存再更新数据库”可能出的问题。假设线程A要更新数据先删了缓存然后准备更新数据库这时候线程B来读数据发现缓存没有于是去数据库查查到的是旧值然后把这个旧值回填到了缓存。接着线程A才更新完数据库。结果是数据库是新值缓存却是旧值而且缓存过期时间还没到的话这个脏数据会一直存在。再看“先更新数据库再删缓存”是否就没问题。假设线程A更新完了数据库还没来得及删缓存线程B这时候来读缓存还在读到了旧值。但这个不一致的时间窗口非常短而且等线程A删掉缓存后后续读就会重建新值。唯一可能出问题的情况是“读请求在删缓存之前发起并且把旧值写回了缓存”但这种概率比前一种方案低得多。所以结论是先更新数据库再删缓存出问题的概率更小、影响的时间窗口更短。这也是Cache Aside模式里推荐的顺序。4.2 延迟双删处理极端并发下的脏数据问题既然“先更新数据库再删缓存”还是存在一个极小概率的脏读问题业界就有了延迟双删这个改进方案。延迟双删的思路很简单更新数据库后删一次缓存然后休眠一小段时间再删一次缓存。第二次删除的目的是处理“第一次删除后、休眠期间有线程把旧数据回填到缓存”的情况。public void update(Shop shop) { // 1. 更新数据库 updateById(shop); // 2. 删除缓存 stringRedisTemplate.delete(CACHE_SHOP_KEY shop.getId()); // 3. 休眠一段时间比如500毫秒 Thread.sleep(500); // 4. 再次删除缓存 stringRedisTemplate.delete(CACHE_SHOP_KEY shop.getId()); }从实际应用来看延迟双删的问题也明显一是多了一次同步休眠接口响应时间会变长二是休眠时间不好设置太短没效果太长影响性能。所以它只适合并发冲突概率较高、但对一致性也比较敏感的场景。对于黑马点评这个项目我的看法是面试时可以提延迟双删作为优化方案但项目本身不实现问题也不大。你把Cache Aside和延迟双删两个方案的取舍讲清楚比写一堆复杂代码更能体现水平。4.3 删除缓存失败怎么办引入重试机制还有一个现实问题Redis操作不是百分百成功的。如果数据库更新成功了但删除缓存时Redis实例刚好抖动这次删除就失败了脏数据就一直留在缓存里。解决办法有两个方向。方向一是重试。把删除失败的操作记录到日志表或消息队列里后台异步任务定期重试。黑马点评里简单处理的话可以用Spring Retry加个重试注解失败后延迟重试几次。方向二是设置较短的过期时间让缓存即使删除失败也能快速过期。这个方案实现简单但只能缩短脏数据存在的时间不能彻底杜绝。真实业务中两种方案经常配合使用短过期时间兜底重试机制保证可靠性。面试能说出这两层已经比大多数候选人强了。5. 缓存更新策略在面试和简历中的正确打开方式5.1 简历上怎么描述缓存更新策略黑马点评这个项目在简历上出现频率极高如何描述缓存这块决定面试官第一印象的好坏。很多人的简历写的是“使用Redis缓存商户信息提升查询性能”。这句话没有任何问题但也没有任何亮点面试官看了不会有追问的欲望。稍微好一点的写法是“基于Cache Aside模式实现缓存与数据库的一致性更新数据时先更新数据库再删除缓存”。这个描述展示了你对缓存更新策略有意识并且知道主流方案是什么。更好的写法是“基于Cache Aside模式解决缓存与数据库一致性问题针对并发场景分析先删缓存与先更新DB的竞态差异采用先更新DB再删缓存的方案并增加缓存过期时间兜底”。这句话的信息量就大了面试官一眼能看出你对这个问题有深入思考。从我的经验来看简历描述不要写太空的术语尽量让每个描述都能对应到具体的代码实现和你的思考过程。面试官追问问不倒才是真本事。5.2 面试官常问的缓存更新策略追问清单黑马点评项目面试中缓存更新策略相关的问题出现频率极高。我整理了一个面试官常用追问清单建议逐条自问自答一遍为什么更新缓存而不是删除缓存先更新数据库还是先删缓存为什么并发下会出现什么极端情况如何优化什么是延迟双删延迟时间怎么定删除缓存失败怎么处理为什么不直接给缓存设置短过期时间去兜底缓存穿透、缓存击穿、缓存雪崩分别怎么解决用互斥锁解决缓存击穿的原理是什么有什么缺点第7、第8个问题虽然名字里带“缓存”但严格来说它们属于“缓存异常问题”和“缓存更新策略”不是一回事。但在面试里这类问题经常被串在一起问因为都围绕Redis缓存展开建议一起复习。回答这些问题时有个核心方法不要只背方案要讲清楚方案的适用条件和局限。比如延迟双删你说“我用了延迟双删”没问题但面试官追问“延迟时间设置多少、为什么”的时候你要能答上来“根据业务的读耗时来估算目的是等读请求回填缓存完成后再删一次”。这种细节才是区分度所在。5.3 真实业务中缓存更新策略的选型经验最后说点项目之外的思考。黑马点评里的商户查询一致性要求并不高所以Cache Aside加过期时间兜底就够了。但真实项目里不同业务对一致性的要求差异巨大不能一套方案走天下。我经手过的一个真实案例是订单状态同步场景用户下单后订单状态从“待支付”变成“已支付”如果缓存没更新用户端一直看到待支付体验就会很差。这种场景下光靠Cache Aside删缓存是不够的通常会用消息队列异步更新缓存或者直接用Canal监听数据库的binlog通过binlog变更来更新缓存。Canal方案的优点是对业务代码无侵入数据库里怎么改缓存就跟着怎么变不需要在业务代码里写删缓存逻辑。缺点是引入了新的组件部署和运维成本都上来了。对于黑马点评这种教学项目完全不必要但你面试时能提到这个方案会显得你对业界实践有了解。选型经验总结成一句话一致性要求越高方案越复杂成本越高。做技术选型时先问业务能不能接受最终一致性如果能Cache Aside就是最优解如果不能再考虑延迟双删、Canal甚至强一致方案。不要一开始就上最重的方案那是过度设计。6. 我从黑马点评缓存更新策略里学到的几点实在体会把黑马点评的缓存更新策略整个梳理下来我自己最深的几个体会想分享出来。第一这个项目把“缓存更新”这件事讲得很务实。它不追求强一致而是用Cache Aside加过期时间来达到最终一致这是绝大多数互联网业务真实采用的做法。理解了这个逻辑再看那些复杂的缓存架构思路会顺畅很多。第二面试中聊缓存更新策略关键不是方案本身而是你对“为什么”的理解。先更新数据库再删缓存很多人知道这个结论但能说清楚并发窗口的人不多。你能画出让对方明白的时序基本就赢了。第三也是我反复在实践里验证过的缓存相关的问题最后几乎都会落到业务场景上。同样一个方案在商户查询和订单状态下判断标准完全不同。黑马点评教会你标准答案但真实工作中你要学会判断什么时候该按标准答案来什么时候该打破常规。如果你正在准备面试建议花一个晚上把这些内容整理成自己的话对着镜子讲一遍能顺下来不打磕巴这块基本就没问题了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门