连载中 9/20

热 Key:一个分片扛下全站流量

2026-05-29 · 3969 阅读 · 0 评论 · 0 赞

集群再大,key 只住一个分片

Redis Cluster 把 key 按哈希槽摊到各个分片上,横向扩容应对的是「总量大」。但热 Key 是另一种病:单个 key 的 QPS 高到打满它所在的那个分片。明星官宣、爆款秒杀、突发热搜,一条 key 每秒被敲几十万次——这个分片的 CPU 和网卡先饱和,落在它身上的其他 key 全部跟着遭殃。更尴尬的是,加机器解决不了:key 哈希到固定的槽、住在固定的分片,扩容不会把它挪薄。

击穿是热 Key 的「过期瞬间」切片,热 Key 是它的「全程放大版」:不只是重建那一下拥挤,是每一秒都在拥挤。

先发现:热 Key 藏在哪

热 Key 的麻烦从发现就开始——它平时不存在,爆发在毫无准备的时刻。发现手段按层级排:客户端统计最直接——在访问封装层按 key 计数、按秒窗口聚合,超过阈值就上报,代价是每客户端一份统计;proxy 层聚合最全面——有代理架构的,代理天然看到全部流量,统计一次全局共享;服务端观测兜底——redis-cli --hotkeys 借助 LFU 计数找热点,监控面板盯单分片的 QPS 与 CPU 突刺。事后的兜底永远慢半拍,客户端埋点加阈值告警是主流选择:热度超阈值的 key 实时推给治理层。

治理一:本地缓存顶在最前

热 Key 的读流量里,绝大多数是重复读同一个不变的数据——这种流量根本不必每次都出进程。本地缓存(Caffeine 挂在应用内存里)挡在 Redis 前面,极热 key 在本地命中,Redis 只承接本地过期后的回源:

// 进程内一级缓存:扛住绝大多数热读
LoadingCache<String, String> hotCache = Caffeine.newBuilder()
    .maximumSize(1_000)
    .expireAfterWrite(3, SECONDS)   // 秒级 TTL,容忍短暂旧值
    .build(k -> redis.get(k));      // 未命中回源 Redis

秒级 TTL 换来的旧值窗口对大多数业务无感,Redis 的压力直接降一个数量级。代价是每个应用实例一份副本、失效通知要额外通道——这本就是多级缓存的雏形,完整展开留给第 16 篇。

治理二:多副本打散

本地缓存扛不住的读,或者本地缓存没法用的场景(实例少、数据大),用空间换分散:同一个数据存 N 份副本,key 带上随机后缀摊到不同槽位上,读的时候随机挑一份:

// 写:一份热数据存 10 个副本
for (int i = 0; i < 10; i++) {
    redis.setex("item:detail:" + id + ":" + i, 1800, toJson(d));
}
// 读:随机挑一个副本,流量摊到 10 个分片
int slot = ThreadLocalRandom.current().nextInt(10);
String v = redis.get("item:detail:" + id + ":" + slot);

分片压力从 1 摊到 10,但写路径要维护 N 份副本的一致性——更新时全量刷新或轮换版本号。两个治理手段不互斥:本地缓存挡第一波,多副本兜第二波,读写分离的只读副本再接一层,热 Key 就从「一个分片扛全站」变回「大家分着扛」。

热 Key 是流量往少数 key 上堆,它的孪生兄弟是数据往少数 key 上堆——一个 key 里塞了十万条成员、一个 string 里塞了几 MB 内容,问题从「读得多」变成「本身太重」。大 Key 这颗定时炸弹,下一篇拆。

503

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

#热Key#本地缓存#分片倾斜#多副本打散#自动探测

评论 (0)

相关推荐

连载中 12/20

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

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

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 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 赞