搞了六年多的后端,我发现自己对缓存的态度一直在变。刚入行那会儿觉得缓存无非就是把热点数据往内存里一塞,读的时候先查缓存,查不到再查库,写回去,完事。后来系统规模上来了,线上问题爆过几轮之后才明白,大型分布式系统里缓存这一环,水比想象中深得多。这篇东西不是教科书,是我这些年踩坑踩出来的经验总结,覆盖缓存选型、一致性、穿透击穿雪崩、热点大Key治理、容量规划,以及一个多级缓存的真实落地案例。适合正在做后端开发、系统架构设计,或者已经被线上缓存问题折磨过的同学。
1. 缓存的两个层次:本地缓存和分布式缓存怎么选
1.1 本地缓存:进程内的最后一层防线
本地缓存就是跑在应用进程内部的那一块内存缓存,Java 生态里最常见的是 Caffeine、Guava Cache,Go 生态里常见的有 bigcache、freecache,本质上都是基于堆内存储的 KV 结构。
本地缓存最大的优势是快,因为它压根不走网络,直接从进程内存里拿数据。一个 HashMap 的 get 操作是纳秒级别的,而一次 Redis 的网络往返,即使在同一机房,也得花上几百微秒到一毫秒。这个差距在高并发场景下会被放大到极致。
但它的问题也很突出。首先是数据一致性难保证,每个服务实例都存了一份自己的副本,某个实例的缓存更新了,其他实例还是旧数据。其次是容量受限,JVM 堆内存是有限的,本地缓存塞太多会挤压业务对象的内存空间,直接导致 GC 频繁,反而拖垮整个应用。
所以本地缓存的定位要清楚:它是用来挡最后一层压力的,而不是用来存全量数据的。适合放那些访问量极高、对短暂不一致容忍度高的热点数据,比如商品详情页的商品名称、价格,用户维度的部分配置信息。
1.2 分布式缓存:跨节点的共享缓存
分布式缓存的核心价值是跨节点共享。所有服务实例访问的是同一份缓存数据,某个节点写入的数据,其他节点能立刻读到,一致性天然比本地缓存强一个档次。
Redis 是当下最主流的方案,没有之一。它的优势很明确:单线程模型配合内存操作,吞吐量极高;支持 String、Hash、List、Set、ZSet 一整套数据结构,覆盖大部分业务场景;主从复制、哨兵、Cluster 集群方案成熟,能支撑几十上百 GB 甚至 TB 级别的数据量。
但在选型时也不能无脑上 Redis,得看清场景。如果只是简单 key-value 缓存,对数据结构和持久化要求不高,Memcached 依旧能打,它内存利用率极高,纯内存操作,性能非常稳定。如果业务对最终一致性要求更高,并且已经重度依赖数据库,Tair 这类阿里系方案也可以纳入考量。还有一点,自建 Redis 的成本不只是机器,还有运维体系,比如监控告警、数据迁移、集群扩缩容,这些隐性成本往往比机器本身更高。
1.3 两个层次怎么搭配才合理
本地缓存和分布式缓存不是二选一,而是协同作战。经典做法是两级缓存架构:应用先查本地缓存,查不到再查 Redis,Redis 也没有就回源数据库。
为什么要这么设计?因为 Redis 虽然快,但在极高 QPS 场景下,每次请求都打 Redis 也会成为瓶颈,特别是网络带宽和连接数。本地缓存相当于在 Redis 前面又加了一层缓冲,把最简单的热点请求拦截在进程内。
需要注意的是,本地缓存的 TTL 必须比 Redis 短。比如本地缓存设 5 秒,Redis 设 10 分钟,这样即使本地缓存和 Redis 数据不一致,也顶多持续几秒,很快能自愈。另一个关键点是,加了本地缓存之后,单个请求的链路变长了,需要对整条链路的耗时做监控
