nginx通过缓存锁、key打散、绕过防护及stale兜底四大机制缓解缓存击穿与雪崩:启用proxy_cache_lock控制并发回源,proxy_cache_key设计实现过期错峰,proxy_ignore_headers等避免隐式绕过,proxy_cache_use_stale保障降级可用。

Nginx 本身不管理业务缓存(如 Redis),但作为边缘代理层,它能通过缓存键设计、锁机制和过期策略,在请求入口处主动缓解缓存击穿与雪崩——关键不是“挡得住”,而是“错得开、压得低、兜得住”。
用 proxy_cache_lock 控制并发回源
当某个缓存 key 失效,大量请求同时到达时,Nginx 默认会让全部请求穿透到后端。启用缓存锁后,只有第一个请求真正回源,其余等待缓存写入后直接命中。
-
必须开启:
proxy_cache_lock on;(默认关闭) -
设好超时:
proxy_cache_lock_timeout 5s;,略大于后端 P95 响应时间,避免长时间阻塞 -
配合 stale 使用:
proxy_cache_use_stale updating;,让等待中的请求可先返回旧缓存,降低用户感知延迟 -
注意前提:该机制只对同一
proxy_cache_key生效;若 key 设计太细(如含随机参数),锁就形同虚设
靠 proxy_cache_key 打散过期时间
缓存雪崩常因成千上万个语义相同的请求,因统一 TTL 在整点/整分批量失效。通过改写 key,把集中过期变成自然错峰。
-
路径归并:忽略 query 参数,强制同类接口共用一个缓存单元
proxy_cache_key "$scheme$host$uri";(丢弃 $args) -
ID 分片取模:对资源 ID 取模,把请求分散到固定桶中
例如按 ID % 4 分为 4 组,每组独立过期,错峰效果明显 -
小时级扰动:嵌入
$time_iso8601_hour,让 key 每小时轮换一次
适合公告页、配置类等容忍小时级 stale 的数据
避免缓存被隐式绕过
很多“击穿”其实不是没缓存,而是请求根本没进缓存层——被 Cookie、响应头或精细 key 悄悄放行了。
-
清理干扰头:前端调试用的 Cookie、
Cache-Control: no-cache等会触发BYPASS,需显式忽略proxy_ignore_headers Set-Cookie;proxy_cache_bypass $arg_nocache $http_pragma; -
覆盖上游响应头:即使后端返回
Cache-Control: private,也可在 Nginx 层强制缓存proxy_cache_valid 200 302 10m;(优先级高于响应头) -
规范化 URI:过滤掉随机参数(如
?t=123456、&_v=2),防止每个请求生成新 key
增强容错与兜底能力
单靠缓存命中率不够,还要考虑缓存不可用时系统是否还能稳住。
-
启用 stale 回退:
proxy_cache_use_stale error timeout http_500 updating;,出错或更新中仍可返回旧内容 -
后台静默更新:
proxy_cache_background_update on;,让过期资源在后台悄悄刷新,不影响用户请求 -
限制低频缓存:
proxy_cache_min_uses 3;,访问少于 3 次的资源不进缓存,减少无效 key 和锁竞争 - 多级缓存协同:Nginx 缓存只是第一道防线,需配合 Redis 的逻辑过期、本地 Caffeine 缓存等形成纵深防御











