nginx通过proxy_cache_use_stale在后端高负载时返回陈旧缓存实现优雅降级,需配置timeout、http_503、http_504、updating触发条件,并配合stable cache_key、proxy_cache_valid覆盖5xx、proxy_cache_background_update与proxy_cache_lock等机制协同生效,通过x-cache: stale验证。

proxy_cache_use_stale 在后端高负载场景下,不是用来“缓解后端压力”的直接手段,而是保障用户访问连续性的关键兜底机制——它不减少请求量,但能避免大量请求因后端响应慢或失败而堆积、超时、报错。
真正起作用的,是它在后端已不堪重负时,把“等不到结果”变成“先给旧结果”,让服务从“不可用”降级为“可用但稍旧”,从而稳住用户体验和系统水位。
后端高负载时,哪些情况会触发 stale?
高负载常表现为响应延迟飙升、连接排队、超时增多、5xx 错误上升。此时以下条件最可能被命中:
-
timeout:当proxy_read_timeout或proxy_connect_timeout被触发(如后端处理超过 3s),Nginx 主动中断连接并启用 stale -
http_503:后端主动返回 503(如限流熔断、线程池满) -
http_504:Nginx 等待后端响应超时,自身返回网关超时 -
updating:缓存正在后台刷新,首个请求已发起回源但尚未写完,其余请求可立即返回旧内容
注意:
error(如连接拒绝)在高负载下较少出现,更多见于宕机;http_500通常由业务异常抛出,非典型负载信号,可酌情保留。
推荐配置:
proxy_cache_use_stale timeout http_503 http_504 updating;
高负载下 stale 要生效,必须满足三个前提
-
缓存本身得存在且可命中
-
proxy_cache_key必须稳定(例如$scheme$host$request_uri,去掉随机参数、Cookie、时间戳) -
proxy_cache_valid至少对 200 和 503/504 设定合理有效期(如proxy_cache_valid 200 503 504 5m;),否则 503 根本不进缓存,stale 就无旧数据可返
-
-
缓存区已正确定义并启用
-
proxy_cache_path已声明(含keys_zone) - 对应
location中明确写了proxy_cache my_cache;
-
-
后台更新与锁机制协同就位(否则 updating 不生效)
-
proxy_cache_background_update on; -
proxy_cache_lock on; -
proxy_cache_lock_timeout 5s;(略大于后端 P95 响应时间)
-
如何让 stale 在高负载下更“聪明”地工作?
单纯返回旧数据只是基础,高负载场景需要更精细的配合:
用
updating+background_update实现“边托底边刷新”
用户看到的是 5 分钟前的缓存,但 Nginx 已在后台悄悄拉新数据。一旦刷新完成,下一个请求就自动命中新内容,无需等待。避免锁竞争加剧雪崩
若proxy_cache_lock_timeout设得太短(如 1s),大量请求快速超时并并发回源,反而加重后端压力。建议设为后端平均响应时间的 1.5–2 倍(例如后端 P95 是 800ms,设5s更稳妥)。对热点接口做差异化 TTL
比如商品详情页缓存设proxy_cache_valid 200 302 3m;,再加 30s 随机抖动(通过$upstream_http_x_ttl_offset或应用层控制),防止集体失效引发锁风暴。兜底空响应也缓存
对查询不存在的数据(如已下架商品 ID),返回404并缓存proxy_cache_valid 404 60s;,彻底拦截穿透请求,减轻后端无效查询。
怎么确认 stale 正在高负载下起作用?
停掉部分 upstream 或人为注入延迟(如用 sleep(5) 模拟慢接口),然后检查客户端响应:
- HTTP 状态码仍是
200(不是504) - 响应头含
X-Cache: STALE(需提前配置add_header X-Cache $upstream_cache_status;) - 响应体内容与故障前一致,未变为空页或错误提示
- 日志中
upstream_cache_status字段显示STALE,且upstream_response_time显著低于正常回源耗时
不复杂但容易忽略。











