这段时间好几个朋友在问系统架构设计里缓存这块到底怎么落地,我整理了一下自己过去几个项目的实践经验,把多级缓存和缓存预热这两块单独拎出来讲讲。这两块看起来是基础功,但真正上线后出问题的,反而是那些“以为很简单”的环节。
不管你是刚转架构方向的后端开发,还是已经在维护高并发系统的工程师,这篇文章应该都能给你一些可以直接抄作业的参考。我尽量把方案选型背后的逻辑也讲清楚,这样你拿到自己项目里做取舍的时候,心里会有底。
1. 多级缓存的整体架构与职责划分
1.1 为什么需要“多级”而不是单级缓存
先聊一个最常见的问题:我只用 Redis 做缓存行不行?说实话,大部分业务场景下,单靠 Redis 确实能撑住,但撑到一定程度就会出现性能瓶颈,而且这个瓶颈往往不在 Redis 本身,而在网络 IO 和序列化开销上。
一个典型的请求链路是:客户端 → Nginx → 应用服务 → Redis → 数据库。每次请求都要经过一次局域网网络往返去取 Redis 数据,极端情况下的耗时能到 5ms 到 10ms。单看这个数字好像不高,但在秒杀、热点榜单、商品详情这类单机 QPS 过万的场景下,积累起来的开销就很可观了。更重要的是,Redis 的连接数、带宽和 CPU 都有上限。我之前遇到过一台 32G 内存的 Redis 实例,CPU 先被打满,内存才用了不到一半。这就是典型的“网络和序列化把 Redis 打垮了,而不是内存不够”。
多级缓存的思路,本质上是在不同的物理距离和访问成本上分层部署缓存,让高频率的热点数据尽量在离用户更近、访问更快的那一层被命中。
从性能数据来看:
| 缓存层级 | 典型组件 | 单次读取耗时 | 成本 | 一致性维护难度 |
|---|---|---|---|---|
| L1 客户端/浏览器缓存 | 浏览器 Cache-Control | 0ms | 极低 | 较难 |
| L2 接入层缓存 | Nginx Proxy Cache / CDN | 1ms 左右 | 低 | 中等 |
| L3 应用本地缓存 | Caffeine / Guava | 0.1ms 以下 | 极低 | 最难 |
| L4 分布式缓存 | Redis / Memcached | 1ms 到 5ms | 较高 | 较易 |
| L5 持久化存储 | MySQL / PostgreSQL | 10ms 以上 | 高 | 无需额外维护 |
1.2 各级缓存在链路中的位置与分工
我在实际架构里通常按“四级缓存 + 数据库”来设计,也就是 L1 到 L4 再加 DB。每一层都有明确的职责,谁负责抗流量峰值,谁负责降低响应时间,谁负责保底,都是提前定好的。
第一级是接入层,主要用 Nginx 或者 CDN 缓存静态资源和部分动态页面片段。这一层解决的是“同一个资源被大量用户重复请求”的问题,它能让请求最远只到达接入层,连应用服务都不用碰。
第二级是应用本地缓存(Caffeine),这一层非常关键。它的特点是访问极快,毫秒级都不到,适合存放那些“变化不敏感、读取极其频繁”的数据。比如商品详情里的分类名称、配置项、用户基础信息等。因为存在每个应用进程内部,所以容量不需要太大,但命中率高得惊人。
第三级才是 Redis 这类分布式缓存。它解决的是多个应用实例之间的数据共享问题,也是热点数据的主缓存层。本地缓存没有命中的请求统一回到 Redis 这里,然后才考虑回源数据库。
第四级是数据库本身,它不叫缓存,但我们设计的时候必须把它当作兜底来考虑。所有缓存层都没有命中的请求会落到数据库,所以数据库的压力直接反映了缓存设计的好坏。
这样的分工有一个明确的好处:每一层都只处理自己最擅长的事。接入层抗大流量访问,本地缓存抗高频重复读,Redis 做分布式共享和跨实例一致性兜底,数据库只处理真正的冷数据。
1.3 一次完整的多级缓存读取流程
为了让你更清楚地理解,我描述一个最简单的读取流程,这里没有代码,但每一步都是实际跑过的逻辑:
用户请求到达网关或接入层后,先检查接入层缓存,命中就直接返回。未命中则转发到应用服务;应用服务收到请求后先查本地缓存,命中就返回并回写接入层缓存;本地未命中就查 Redis,命中就返回并写回本地缓存;Redis 也没命中的话,才允许查询数据库,查到的结果先写 Redis,再写本地缓存,再回写接入层,最后返回给用户。
这套流程真正跑起来之后,你会发现一个很有意思的规律:热点数据的响应时间会从 10ms 级别逐渐收敛到 1ms 甚至 0.5ms 级别。因为你每一次回源成功都会把数据回填到更上层的缓存中,下一次同样的请求就会被更快的层级拦下来。这就是“缓存预热”的被动形态,它每天都在发生,只是我们没有主动控制它而已。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 缓存选型与参数设计的关键依据
2.1 各层缓存的技术选型对比
本地缓存这一层,我长期用下来最稳的是 Caffeine。它的底层是基于 Java 的 ConcurrentMap 演进出来的,支持按大小、按时间、按引用三种淘汰策略,而且有自己的异步刷新机制,可以做到“读多写少
