顶流明星官宣导致服务器宕机?拆解 100 万 QPS 热点流量击穿 Redis 的真实面试现场:从为什么“加机器没用”,到多级缓存架构,再到热 Key 动态发现与自适应防御。
有兄弟分享美团二面,被问挂了。 面试官问:“假设有个千万级粉丝的顶流明星突然官宣,瞬间涌入 100 万 QPS 的并发流量去查询这条动态。如果我们把数据缓存在 Redis 里,你怎么应对? ”
这哥们寻思平时 Redis 八股文背得挺熟:“简单啊!单机扛不住咱们就上 Redis Cluster 集群,弄个 10 台乃至 20 台机器,横向扩容,别说 100 万,再来一倍也能扛下来!”
面试官冷笑一声,连续抛出三个灵魂拷问:
1.“你知道 Redis 集群的数据路由规则吗?同一个 Key 只会落在一台节点上,这 100 万 QPS 全打在这一个节点上,另外 19 台全在围观,机器加得再多有什么用?”
2.“退一步讲,单点 Redis 官方理论值才 10 万 QPS,真实业务带上大 Value 网卡早就被打爆了。单节点挂了触发主从切换,这 100 万并发瞬间穿透打进 MySQL,数据库直接熔断。”
3.“你怎么在系统崩掉之前,提前发现它是‘热 Key’?”
其实,这道题考的是 “超高并发下的热点缓存架构设计” 。接下来带你拆解热 Key 防御的 3 种方案。
一、 为什么传统的 “加机器扩容” 是送人头?
100 万 QPS 读同一个 Key,在 Redis Cluster 架构下,请求会通过 Hash 算法,精准地打到集群中的唯一一个分片(Node) 上。
很多人以为单机能抗 10 万 QPS,但那是极小 Value 的理想情况。在真实业务中,几 KB 的 Value 乘以几十万的 QPS,瞬间就会造成千兆网卡阻塞、CPU 飙升到 100%。 面对单点热 Key,靠横向扩充 Redis 节点是毫无意义的,必须从 “流量拦截”和“数据打散” 上下功夫。
二、 核心架构:3 种主流解法
解法 1:散列分片法
既然一个 Key 只能落在一个节点上,那我们就把它变成多个 Key。
- 方案: 给这个热 Key 加上随机后缀,比如 coupon
#1到coupon#100,将同一份数据复制 100 份存进 Redis 集群的不同节点。 - 效果: 客户端请求时,在应用层生成一个 1~100 的随机数,拼接到 Key 后面去查。这样 100 万 QPS 就被均匀打散到了整个集群上。
- 局限性: 严重浪费 Redis 内存,且一旦明星修改了动态,修改 100 个 Key 的数据一致性维护成本极高。
解法 2:多级缓存架构
这是大厂应对突发流量的标配.
- 请求到达应用服务器,先查 Caffeine 本地缓存,命中直接返回。
- 未命中,再去查 Redis,拿到数据后写回本地缓存(设置极短过期时间,比如 3 秒)。
- 效果: 100 万 QPS 打向 100 台 Tomcat 应用服务器。因为有本地缓存,99% 的请求都在 Tomcat 内存中被挡住了。最终落到 Redis 上的穿透请求可能只有几千 QPS,稳如泰山。
解法 3:热 Key 动态发现与自适应
面试官最喜欢抠细节:“你怎么知道哪个是热 Key?等你发现宕机了再去发版加本地缓存,早就开除你了。”
- 方案: 搭建热点探测框架(如京东的
Jdhotkey)。 - 原理:
- 探测器: 在客户端或代理层基于滑动窗口进行统计,比如“某 Key 在 1 秒内被访问超过 1000 次”,自动判定为热 Key。
- 推送器: 发现热 Key 后,立刻通过长链接或 MQ 推送到所有的业务集群节点。
- 自适应: 业务代码自动将该 Key 写入 JVM 本地缓存。
- 效果: 系统具备了 “自动免疫” 能力,在顶流爆雷的 1 秒钟内,系统自动完成防御闭环。
三、 最后的“防杠”指南
面试官看你懂了多级缓存,一定会追问极端场景:
Q1:加了本地缓存后,如果明星在这 3 秒内又改了文案,各台机器上的本地缓存不一致怎么办?
答: “大流量的 C 端读场景,必须拥抱最终一致性。第一,我们会给本地缓存设置极短的 TTL(如 3 秒),3 秒的延迟在微博这种场景完全可以接受;第二,如果业务要求高,可以通过 Redis Pub/Sub 或 MQ 广播,主动通知各个 Tomcat 节点使其本地缓存失效。”
Q2:如果缓存刚好同时失效,瞬间发生“缓存击穿”怎么办?
答: “我会给重建缓存的代码加上互斥锁或者使用逻辑过期机制。保证同一时刻只有 1 个线程去查数据库并写回 Redis,其余几十万个线程短时间休眠重试或直接返回降级的默认数据,死保 MySQL 的命。”
技术面试考的从来不是你会不会启动几个 Redis 节点,而是你对分布式缓存的资源分配与极端防御的敬畏。
在百万级 QPS 的洪峰面前,能看透 Hash 槽与网卡带宽的局限,并熟练运用多级缓存和热点探测,这才是你和普通开发者拉开差距的关键。