缓存分层设计的核心思路,是把不同热度资源放进不同层级的存储,让热资源靠近用户、快速响应,让冷资源远离核心、降低开销,二者各得其所。
缓存不是越上越好,也不是越全越好,热点资源放本地内存、冷资源放Redis或数据库,听起来简单,实际落地需要围绕命中率、一致性、成本三个维度做取舍,下面结合实际场景拆解。
热点资源如何被识别并送进“快车道”
热点数据的判定标准
业内专家指出,多数线上系统的热点请求具备两个特征:单一Key的QPS显著高于均值,以及访问集中在极短时间窗口内,比如一场秒杀活动开始后,某个商品库存Key的访问量在几秒钟内飙升至平时的数百倍,这就是典型的热点。
识别热点不能靠拍脑袋,常见做法是:
- 在业务入口埋点,统计Key的访问频次,滑动窗口按秒级聚合。
- 当某个Key的QPS超过预设阈值,比如每秒500次,就从普通状态升级为热点状态。
- 利用Redis的ZSET结构维护一个有序的访问热度榜单,定期清扫衰减数据。
这套识别逻辑不会占用大量资源,在Nginx层或网关层做过滤即可,一旦锁定热Key,就把它的数据副本推送到更靠近用户的缓存层。
热点资源该放在哪一层
热点数据的存放位置决定了响应速度,用户浏览器≈0毫秒网络延迟,CDN边缘节点≈10-30毫秒,Nginx本地缓存≈0-1毫秒进程内访问,Redis≈1-5毫秒内网往返,数据库≈5-20毫秒磁盘IO。
高并发场景下,Hot Key的响应速度直接影响系统吞吐量,对于QPS达到万级的资源,跨网络访问Redis仍然是不小的开销,合理路径是:网关层/应用层本地缓存作为第一道防线,Redis作为第二道防线。
冷资源为什么不该占用高性能缓存
冷资源的真实成本
冷资源的访问频率低,但数量庞大,一个电商平台可能有上千万件商品,但每天有曝光流量的只有几十万件,如果把所有商品都缓存进Redis,内存消耗将是一个天文数字。
据统计,在典型的业务系统中,80%以上的数据属于低频访问的冷数据,这些数据放在Redis里,命中率可能低于5%,却占用了约60%的内存空间,这就是典型的资源错配。
冷资源的正确去处
冷资源不该完全放弃缓存,但应当放在更廉价的存储层,顺序依次是:
- 数据库或对象存储作为最终数据源,比如MySQL、OSS。
- 大容量缓存层最合适放冷数据,用Redis但设置更短的过期时间,或用本地文件缓存。
- 加一层CDN回源缓存,让冷数据在到达应用层之前就被拦截。
减少冷数据缓存成本的关键是按访问频率动态调整TTL,高频访问的数据自动延长过期时间,低频数据随着活动度下降逐步缩短缓存寿命,最终自动淘汰。
缓存分层设计的原则与实操路径
不同层的定位各有分工
一个典型的五层缓存模型是这样的:
- 浏览器层:存放静态资源(图片、CSS、JS),通过HTTP缓存头控制。
- CDN层:缓存热点静态资源和部分动态API响应,边缘节点就近返回。
- 网关/负载均衡层:Nginx或OpenResty层做局部缓存,应对突发流量。
- 应用本地缓存层:Caffeine或Guava Cache,进程内访问,速度最快。
- 分布式缓存层:Redis Cluster,承载跨节点的共享缓存数据。
每一层解决不同问题,浏览器层减少请求数,CDN层减少回源带宽,本地缓存减少网络IO,Redis解决多实例共享问题。
数据一致性如何控制
跨层缓存最怕的是数据不一致,多级缓存同步时,业界常见的做法是:
- Cache Aside模式:先更新数据库,再删除对应缓存。
- 延迟双删:先删缓存、更新数据库、休眠一小段时间后再次删除缓存。
- 版本号机制:在缓存值中嵌入版本号或时间戳,读取时对比是否过期。
- 消息队列异步失效:通过MQ广播缓存失效事件,各层监听后主动清理。
不同层级对一致性的容忍度也不同,浏览器和CDN层的静态资源通常接受分钟级延迟,本地缓存与Redis则要求秒级甚至毫秒级一致,更新数据库后立即异步刷新本地缓存与Redis,而不是等过期时间到了才自然淘汰。
热点key缓存击穿问题如何解决
缓存击穿指某个热点Key在过期瞬间,大量并发请求同时穿透到数据库,典型应对方案有三个:
- 互斥锁(Mutex Key):当缓存失效时,只允许一个请求去重建缓存,其他请求等待结果复用。
- 逻辑过期:缓存中不存真实过期时间,而是存一个逻辑过期时间戳,后台异步刷新,读线程永远拿旧值或触发异步更新。
- 多级兜底:Redis中的热点Key过期后,本地缓存仍在有效期内,保证业务不受影响。
其中本地缓存兜底对高并发场景最具实用性,热点Key在Redis中过期后,本地缓存还能扛住接下来几秒到几分钟的流量,这段时间足够后台重建数据。
分层设计如何应对真实流量场景
用一个实际场景来说明:一个新闻资讯App的首页信息流,默认加载20篇文章封面图和标题,热点时刻通常发生在每天早上8点到10点,有大量新内容被频繁刷新。
分层之后的效果
- 用户手机端缓存首屏静态结构,20分钟不刷新。
- CDN缓存封面图和标题JSON数据,TTL设为10分钟。
- 网关层用Lua脚本缓存当前热度前100的资讯ID列表,TTL设为5分钟。
- 应用层本地缓存热门资讯详情,热点状态时自动延长TTL。
- Redis缓存全量资讯数据,普通Key的TTL设为一小时,冷数据自然淘汰。
最终表现是:90%以上的请求会被浏览器、CDN、Nginx三层拦截,应用层访问的Hot Key全部命中本地缓存,Redis的压力大幅下降,数据库基本无感。
Redis缓存与本地缓存如何选择
很多团队会纠结该多依赖Redis还是本地缓存,Redis缓存与本地缓存的实际差异需要考虑以下几点:
- 数据量是否超过单机内存,如果热点数据量大,比如超过1GB,就必须用Redis。
- 是否需要跨节点共享,订单状态、用户登录态等需要全局一致的数据,优先Redis。
- 是否允许短暂不一致,商品描述、资讯详情允许秒级差异,本地缓存更高效。
- 是否容易实现失效通知,本地缓存需要借助Redis Pub/Sub或消息队列广播更新事件,代码复杂度更高。
合理的方式是两者配合而非二选一。本地缓存做热点,Redis做全集,二者通过热点识别机制联动。
缓存穿透与雪崩在分层设计中的隔离逻辑
缓存穿透与缓存雪崩是不同的故障,但分层设计能同时缓解两者。
缓存穿透指查询一个不存在的数据,请求直接打到数据库,方案:布隆过滤器前置拦截,或者缓存空值并设置极短过期时间。
缓存雪崩指大量Key在同一时间过期,导致请求集中打到数据库,方案:过期时间加随机抖动,热门Key设置不同过期区间;同时依靠多级缓存,Redis大面积失效后本地缓存还能兜底。
分层设计中,每层各自设置独立的淘汰策略:
- CDN层按静态资源的刷新规则管理。
- 本地缓存使用LFU策略,优先淘汰低频访问的数据。
- Redis按内存淘汰策略(allkeys-lfu),动态调整全局Key的生命周期。
当一层故障时,下层仍然可以独立工作,不会形成级联雪崩。
Q&A:缓存分层设计常见疑问
热点key缓存击穿问题如何解决
击穿的本质是热点Key过期瞬间缺乏保护,推荐优先级排序:第一,加互斥锁,只放一个请求回源重建;第二,给热点Key设置逻辑过期,后台持续刷新真实数据;第三,叠加本地缓存作为兜底,确保Redis失效时仍有备用数据源,三种策略可以并存。
冷资源是否完全不需要缓存
不是,冷资源的访问频次低但不为零,完全不缓存会导致偶尔的用户请求穿透到数据库,拉长响应时间,冷数据可以放在Redis的大容量节点或者使用Redis的RDB持久化内存数据库中,设置较长但有限的TTL,并且定期扫描淘汰访问频率接近零的数据,这样既保留快速响应能力,又不让冷数据长期占据宝贵的性能层空间。
Redis缓存与本地缓存如何选择
选择取决于三个条件:数据共享需求、数据量大小和一致性容忍度,需要被多个应用实例共享的状态数据,选Redis;单实例内热点极集中的数据,选本地缓存;两者混合部署时,用热点识别模块将高QPS的Key自动复制到本地,其余数据留在Redis。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/643379.html





