报错是这么冒出来的。首页推荐接口把 30 万条结果缓存在 Redis,key 的 TTL 都是 3600 秒,上线后每小时整点数据库 QPS 从 200 冲到 6000,连接池被打满,接口超时 30 秒,监控显示 Redis 命中率在同一秒掉到 0。
中间走过弯路,把「空结果没缓存,请求穿透到底层」当成主因,改完现象没变。
真正麻烦的地方在于,几个原因的现象几乎一样。所有 key 在同一批预热,过期时间完全相同。也可能是没有互斥重建,300 个并发同时回源。
不容易想到的是,TTL 没加随机抖动,过期时间同步。
先看一批 key 的剩余 TTL 是否一致
redis-cli --scan --pattern 'rec:*' | head -20 | xargs -I{} redis-cli ttl {}
给 TTL 加 0 到 300 秒随机抖动
python -c "import random;print(3600+random.randint(0,300))"
然后用 SETNX 做互斥重建只放一个请求回源
redis-cli set lock:rec:home 1 nx ex 10
压测整点过期场景看 QPS 曲线
python cache_stampede.py --keys 1000 --ttl 60 --concurrency 300
怎么算真的好了,看的是同一批现象。整点前后数据库 QPS 稳定在 300 以内,Redis 命中率不再掉零,接口 P99 回到 200 毫秒。
同一批预热的 key 必然同时过期,抖动是最便宜的办法;热点 key 还要加互斥重建,否则抖动只能把尖峰摊薄,不能消除。这类问题最好一次改到位,反复小改反而容易把原因搅混。
