Nginx proxy_cache_lock 是应对缓存击穿的轻量方案,需同时满足定义keys_zone、启用proxy_cache、开启proxy_cache_lock三条件;key须统一(如剔除$args)、配timeout与stale兜底,并通过X-Cache-Status验证。

缓存击穿不是“缓存失效了就出事”,而是大量并发请求在某个热点 key 失效的瞬间,同时发现缓存 MISS,一拥而上打到后端,造成瞬时压力激增。Nginx 本身不提供分布式锁,但 proxy_cache_lock 是它应对这一场景最直接、最轻量的防线——关键在于怎么配得准、用得稳。
必须配齐的三个启动条件
缺一不可,否则 lock 完全不触发:
- 在
http块中定义带keys_zone的缓存区,例如:proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g; - 在目标
location中启用缓存:proxy_cache my_cache; - 在同一 location 中开启锁:
proxy_cache_lock on;
让锁真正锁住同一类请求
lock 只对完全一致的缓存 key 生效。key 分裂 = 锁失效。常见破防点:
- 默认
proxy_cache_key含$args,/api/user?id=1和/api/user?id=2就是两个 key → 改为:proxy_cache_key "$scheme$host$uri";(剔除 query) - 前端加了
?t=1713086520或Cookie: session=abc→ 不要让这些进 key,必要时加:proxy_ignore_headers Set-Cookie; - 想统一锁定所有用户查询?可硬编码 key:
proxy_cache_key "$host:/api/user";
不能只靠锁,得有超时和兜底
锁只是串行化,不是万能解药。没 timeout 和 stale,反而会放大问题:
-
proxy_cache_lock_timeout 5s;:设 3–8 秒。太短(如 1s),等待请求快速超时并重试,又引发新一波回源;太长,用户卡死 -
proxy_cache_use_stale error timeout updating http_500 http_502;:首个请求回源失败或超时,后续请求可立即返回旧缓存,避免集体阻塞 -
proxy_cache_valid 200 30s;:延长热点接口有效期,从源头减少 MISS 次数,比补 lock 更治本
验证锁是否真起作用
别猜,看响应头:
- 加一句:
add_header X-Cache-Status $upstream_cache_status; - 用
curl -I连发两次相同请求:
第一次返回X-Cache-Status: MISS,
第二次若返回HIT(且时间间隔远小于后端响应时间),说明 lock 成功拦截并复用了结果


















