连载中 15/16

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

2026-05-22 · 4799 阅读 · 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)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞