后端多级缓存怎么设计才能扛住热点流量
后端多级缓存扛热点流量的核心配方是:**L1 进程内本地缓存拦住 95% 以上读请求 + L2 Redis 集群承接跨节点共享 + 热点 Key 主动打散 + 短 TTL 加广播失效保证最终一致**。热点流量绝不能被放到 Redis 的单个分片上,那是最常见的翻车点——Redis 单分片 8 万到 10 万 QPS 就是天花板,而一台 8 核机器上的 Caffeine 本地缓存能跑到 100 万 QPS 以上。
多级缓存到底分几层,每层放什么?
结论:三层足够,L1 本地缓存、L2 Redis、L3 数据库,层级越靠前容量越小、速度越快。
- L1 本地缓存(Caffeine / Guava Cache):存放在 JVM 堆内,读取耗时在 100 纳秒到 1 微秒之间。只存热点数据,容量控制在 1 万到 10 万条,单实例占用堆内存 50MB 到 200MB。
- L2 Redis:网络往返 0.2 到 1 毫秒,单分片 8 万 QPS,集群按 key 哈希分片后可线性扩展。存全量业务数据,容量按业务定,一般设 TTL 30 分钟到 2 小时。
- L3 数据库:只做最终兜底,QPS 压在 2000 以下,靠主从读写分离撑住。
关键点:L1 命中率低于 80% 就说明缓存策略有问题,要么容量太小被 LRU 频繁淘汰,要么 TTL 太短。
本地缓存选型和参数怎么定?
结论:选 Caffeine,它的 W-TinyLFU 淘汰算法在真实业务负载下命中率比 LRU 高 10 到 20 个百分点。
参考配置:maximumSize(50000)、expireAfterWrite(5, TimeUnit.SECONDS)、打开 recordStats() 观察命中率。TTL 设 3 到 5 秒是甜点区间——太短本地缓存没意义,太长数据不一致窗口拉大。本地缓存必须设容量上限,不设 maximumSize 的本地缓存就是内存泄漏。
本地缓存和 Redis 不一致怎么解决?
结论:用「写库后广播失效 + 短 TTL 兜底」,放弃强一致,接受 10 到 100 毫秒的最终一致窗口。
做法是:数据变更时,先更新数据库,再发一条失效消息(Redis Pub/Sub、RocketMQ 广播模式或 Nacos 配置监听都可以),所有实例收到后 invalidate(key) 删掉本地缓存。Pub/Sub 不可靠会丢消息,所以本地缓存必须同时设 3 到 5 秒 TTL 做兜底——即使广播全丢,最多 5 秒后数据也会自己过期重载。
热点 Key 怎么打散?
结论:热点 Key 分两种打法,读热点靠本地缓存 + key 加后缀分片,写热点靠异步合并写。
- key 加后缀分片:把一个
item:1001拆成item:1001:0到item:1001:7共 8 份,读时随机取一份,写时全写 8 份。Redis 侧压力立刻降到 1/8,代价是内存放大 8 倍,只对少量真热点用。 - SingleFlight 合并回源:同一个 JVM 内,1000 个并发请求同一个 key,只放 1 个去查 Redis 和 DB,其余 999 个等待结果。Go 用
golang.org/x/sync/singleflight,Java 用 Caffeine 的get(key, mappingFunction)天然带这个语义。 - 逻辑过期:value 里存业务过期时间而不是靠 Redis TTL,读到过期数据先返回旧值,再异步起线程重建。这个方案能保证热点 key 永不阻塞。
穿透、击穿、雪崩分别怎么防?
结论:三个问题三种解法,不要混用一套方案。
- 穿透(查不存在的数据):缓存空值并设 60 秒 TTL,或用布隆过滤器,误判率配 0.01%。
- 击穿(单个热点 key 过期瞬间):逻辑过期 + SingleFlight,热点 key 直接设成永不过期。
- 雪崩(大批 key 同时过期):TTL 加随机扰动,公式是
baseTTL + random(0, baseTTL * 0.1),把过期时间摊开。
怎么发现热点 Key?
结论:靠事后统计而不是事前猜测,滑动窗口计数最实用。
可用方案有三档:一是 Redis 4.0 以上的 redis-cli --hotkeys 配合 LFU 淘汰策略,只能事后看;二是京东开源的 hotkey 框架,客户端上报 + 计算节点聚合,秒级发现热点;三是自己埋点,在本地缓存层做 Count-Min Sketch 计数,超过阈值(比如 1000 次/秒)自动升级为热点并加载进本地缓存。
总结一下:多级缓存抗热点的顺序是「先让本地缓存吃掉绝大多数读请求,再把剩余流量在 Redis 分片间打散,最后用完兜底」。落地时优先做三件事——Caffeine 设好 maximumSize 和 3 到 5 秒 TTL、加广播失效加 TTL 兜底保证一致性、用 SingleFlight 合并回源。热点打散是最后的杀手锏,别一上来就用 key 分片,那会白白放大 8 倍内存。
原文链接:https://www.gj0.com/thread-1080.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。