热点被疯狂刷爆?几招实操方法守住服务器不崩

上周朋友的微信小程序后台突然告急:一个临时上线的「限时领券」入口,3分钟内涌入20万请求,数据库CPU直接飙到98%,用户点不动、页面白屏、客服电话被打爆。问题出在哪?不是流量大,是热点被滥用——那个券码生成接口成了单点瓶颈,所有请求全挤在同一个缓存键、同一条SQL、同一个Redis连接池上。

先搞清什么叫“热点被滥用”

不是所有高流量都叫热点。真正的热点,是集中在极小粒度资源上的突发性、集中式访问。比如:
• 用户ID为10086的个人主页被爬虫盯上,每秒300次GET;
• 商品SKU=88927345的库存查询接口,在秒杀开始瞬间扛住1.2万QPS;
• 某个配置项key='app_banner_v2'被前端轮询调用,每10秒一次,全走DB读。

别硬扛,从三道防线拆解

第一道:前置拦截,把无效流量拦在门外

用Nginx做最轻量的过滤。比如限制单IP对热点接口的访问频率:

limit_req_zone $binary_remote_addr zone=hot_api:10m rate=5r/s;
server {
location /api/v1/coupon/generate {
limit_req zone=hot_api burst=10 nodelay;
proxy_pass http://backend;
}
}

再加一层简单校验:带签名的请求头、有效时间戳、基础参数非空判断。恶意脚本连第一关都过不去。

第二道:缓存分层,不让压力直通后端

别只靠Redis。把热点数据按热度分级:
• 一级:本地缓存(Caffeine),TTL设短(如30秒),扛住瞬时毛刺;
• 二级:Redis集群+逻辑分片,比如把用户ID哈希后路由到不同key:user:profile:hash(10086):v2,避免单key热;
• 三级:DB加读写分离+只读从库负载均衡,主库绝不接查。

遇到突发流量,本地缓存击穿时,用“互斥锁+逻辑过期”兜底,而不是直接打DB:

// 伪代码示意
String key = "user:10086:profile";
Object data = localCache.get(key);
if (data == null) {
if (tryLock("lock:" + key)) {
// 查DB,写入本地缓存+Redis,设置逻辑过期时间(如2分钟)
refreshCache(key, dbQuery());
unlock();
} else {
// 等100ms,再读一次本地缓存(可能已被其他线程写入)
Thread.sleep(100);
data = localCache.get(key);
}
}

第三道:业务降级,该砍就砍,别死撑

热点不是技术问题,更是产品决策问题。上线前问一句:这个功能真的需要实时、强一致、全量返回吗?
• 券码生成可以预生成10万张,存在Redis List里,pop一个发一个,不用每次算;
• 个人主页的粉丝数,显示“10w+”比实时查328745更稳;
• 秒杀商品页的库存,前端显示“仅剩XX件”,后端用原子减,超卖由订单创建阶段二次校验兜底。

最后提醒一句:监控不能只看QPS和响应时间。在Prometheus里加个告警规则,比如“redis_key_hits{key=~"user:.*:profile"} > 5000持续1分钟”,比等报警电话响起来再救火强十倍。