fastcgi_cache_lock 是布尔开关,启用后多请求命中未缓存url时仅首请求回源,其余阻塞最多5秒(不可配置),适用于后端快、并发miss高且需防击穿的场景;慢接口应关闭并配合stale更新等策略。

fastcgi_cache_lock 是 Nginx 应对瞬间请求风暴(比如秒杀、热点新闻、促销开抢)时,防止后端 PHP 进程被大量并发 miss 请求打垮的关键机制——但它不是“万能锁”,用对了才起作用,配错了反而雪上加霜。
它怎么挡住风暴?核心就一条逻辑
当多个用户同时访问一个还没缓存的动态页面(比如 /product/123),Nginx 不会让所有请求都冲向 PHP-FPM。启用 fastcgi_cache_lock on; 后:
- 只放行第一个请求去后端执行(生成新缓存)
- 其余请求在 Nginx 内存里排队等待,不发往后端
- 等第一个请求完成并写入缓存后,所有等待请求统一从缓存返回
- 如果等超 5 秒(硬编码上限),就各自放弃等待、回源——不会卡死,也不会堆积连接
真正起效的前提:缓存必须“锁得住”
锁不住,等于没锁。常见失效原因:
-
cache key 不一致:比如 URL 带随机参数
?v=123456或用户 ID,每个请求 key 都不同,锁完全不生效 -
响应头干扰缓存:PHP 返回了
Set-Cookie或Cache-Control: no-cache,导致 Nginx 拒绝缓存,反复 miss -
没配缓存区或没启用缓存:
fastcgi_cache_path和fastcgi_cache my_zone;缺一不可
建议 key 写成:fastcgi_cache_key "$scheme$host$request_uri";,并加 fastcgi_ignore_headers Set-Cookie;
别碰不存在的参数:timeout 和 age 都是假的
Nginx 官方所有稳定版本(包括 1.24.x / 1.26.x)都不支持:
-
fastcgi_cache_lock_timeout(配置会静默忽略或启动失败) -
fastcgi_cache_lock_age(根本不存在,不是 typo,是彻底没这个指令)
它的等待就是固定 5 秒,源码写死,没法调。所谓“自动释放死锁”“动态老化”都是误解——Nginx 的锁是轻量内存标记,不是系统级互斥锁,不存在死锁可能。
比 lock 更稳的组合:stale + background update
单靠 lock 只能“减流量”,要真正扛住风暴,得让它“不卡用户、也不压后端”:
-
fastcgi_cache_use_stale updating error timeout http_500;:缓存正在更新、或后端出错时,继续返回旧内容 -
fastcgi_cache_background_update on;:过期缓存由首个请求异步重建,其余请求照常读旧缓存 -
fastcgi_cache_lock on;(配合上面两项):避免多个请求同时触发后台更新
这样,哪怕 PHP 响应慢一点,用户也几乎感觉不到延迟,后端压力还被摊平了。











