连载中 15/16

热 key:当所有流量涌向同一个 key

2026-05-22 · 4796 阅读 · 0 评论 · 0 赞

官宣 8 点档:40 万 QPS 涌向一个 key

娱乐频道同事提前把「官宣」词条缓存预热好了,整点一到,流量准时砸下来:词条详情接口 topic:detail:9527 一个 key 扛了 40 万 QPS。奇的是 Redis 集群里其它节点都很闲,就这一个节点网卡被打满——这时才体会到 Cluster 的一个「盲区」:分片按槽切数据,而一个 key 永远只住在一个节点上,集群的分流能力对单个 key 无效。

加从库行不行?行,但算笔账:40 万 QPS 平摊到 20 个从库,每个 2 万——为了一个词条买 20 台机器,老板的表情会很难看。热 key 的解法不在「堆机器」,而在把流量摊开。先得找得到它。

先找到它:探测三招

热 key 最阴险的地方是它平时不存在,爆的时间点又最要命。三招探测:

  • 客户端埋点:在访问 Redis 的入口组件上加滑动窗口计数,按 key 统计 QPS,超阈值告警。实时性最好,也是大厂的标配做法。
  • redis-cli --hotkeys:官方自带,靠 LFU 计数找最热的 key——注意必须像第 9 篇说的那样配 allkeys-lfu 系策略才有效,LRU 模式下没有访问频率数据。
  • 代理层统计:如果业务走 proxy 访问 Redis,在 proxy 统一计数,多语言应用都能覆盖,代价是多一层组件。
# LFU 模式下,列出最热的 key(适合离线巡检)
redis-cli --hotkeys

顺带厘清一对概念:大 key 是体积问题(一个 key 太重,第 3 篇讲过治理),热 key 是流量问题(一个 key 被访问太多次)。两者常结伴出现——热搜词条既大又热,那就是双重打击。

招式一:key 打散

既然一个 key 只能住一个节点,那就复制出 N 个分身:topic:detail:9527#1#10,内容完全相同,但经过 CRC16 会落进 10 个不同的槽——40 万 QPS 被摊成每槽 4 万。写的时候 N 个分身全写,读的时候随机挑一个:

// 写:N 个分片全写,内容一致
// 读:随机挑一个分片,流量被摊平
String shard = "topic:detail:9527#" + ThreadLocalRandom.current().nextInt(1, 11);
String value = redisTemplate.opsForValue().get(shard);

它的甜点是读依然强一致(分身内容相同),代价是写放大和业务代码要感知分片数。适合少写多读的场景——热搜词条恰好都是这种。

招式二:本地缓存,把流量挡在门外

更狠的思路是让这些请求根本别到 Redis:应用进程内放一层 Caffeine 本地缓存(第 6 篇多级缓存的 L1),热 key 进程内命中,Redis 压力归零。关键参数是 TTL 必须设短——3 到 5 秒,热点场景容忍几秒的脏读,换来 Redis 侧的彻底解放:

// 进程内扛热点:TTL 只有 3 秒,容忍短时脏读
Cache<String, String> localCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(Duration.ofSeconds(3))
        .build();

它治本,但也有代价:每个应用实例各存一份(内存换带宽);数据更新后各实例要在 TTL 窗口之后才看到新值,强一致需求别用这招。另外本地缓存过期那一瞬间,大量请求还是会涌向 Redis——别忘了第 5 篇的互斥锁或 singlefly 给回源上一把锁。

兜底:限流与降级

探测有延迟、打散要改造、本地缓存有脏读窗口——三招都来不及上的时候(比如热点毫无预兆地爆了),最后一道防线是第 6 篇讲过的限流降级保险丝:对该接口限流保住 Redis 和下游数据库,被限掉的请求返回默认数据或友好提示。难看,但活着。

方案怎么选

方案生效速度实时性代价改造成本
扩从库 + 读写分离中(要扩机器)
key 打散无(读一致)中(读写都要改)
本地缓存秒级脏读
限流降级立即用户体验受损

实战的组合拳通常是:客户端埋点常开探测 + 可预测的热点(活动词条)提前预热进本地缓存和分片 + 突发热点靠本地缓存自动兜住 + 限流保命。四层防线,和第 4 篇防穿透的四道防线异曲同工——缓存这门课,学到最后都是「信任边界」的设计。


热 key 讲完,这个系列的主体内容就齐了。下一篇是收官复盘:把 15 篇的碎片串成一张作战地图——从一次接口优化开始,到穿透、击穿、雪崩、一致性、分布式锁,再到主从、哨兵、集群与性能调优,每个问题该翻回哪一篇、先查哪个方案。下篇见,给整个系列画个句号。

咖啡凉了,记得趁热喝。

503

10 年全栈工程师 · 503咖啡馆主理人

#Redis#热key#本地缓存#热点问题#高并发

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞