Nginx 本身不支持本地缓存 Redis,所谓“Nginx + Redis 本地缓存”是误解;实际可行方案只有两种:一是通过 redis2_module 或 OpenResty 的 lua-resty-redis 做透传查询(无缓存),二是用 proxy_cache 等内置缓存机制将 Redis 作为后端数据源,分层实现真正本地缓存。

直接说结论:Nginx 本身不能“本地缓存 Redis”,它没有内置 Redis 客户端;所谓「Nginx + Redis 本地缓存」是常见误解,实际能落地的只有两种路径——要么用 nginx_http_redis2_module(已废弃)或 OpenResty 的 redis2 / lua-resty-redis 模块做透传查询,要么在 Nginx 层加一层真正意义上的本地缓存(如 proxy_cache),再把 Redis 当后端数据源。强行混搭不区分层级,反而会放大穿透风险。
为什么 nginx.conf 里写 redis2_pass 不等于“本地缓存”
Nginx 的 redis2_pass(来自第三方模块)只是把请求转发给 Redis,全程无缓存、无合并、无降级。它和直接 curl Redis 几乎等价,只是少了一次 TCP 连接建立开销。一旦 Redis 响应慢或超时,Nginx 就原样暴露给上游,且每个请求都独立打到 Redis —— 这恰恰是缓存穿透/击穿放大的温床。
常见错误现象:
- 配置了
redis2_pass 127.0.0.1:6379,但没配error_page 502 504 = @fallback,Redis 一抖动,整个接口 502 - 所有请求都走
GET key,对空值(如id=-1)不做拦截,导致每秒几千次无效 Redis 查询 + 后续 DB 查询 - 没设
redis2_read_timeout,Redis 卡住时 Nginx worker 线程被长期占用,连接数迅速打满
真正可行的“Nginx 本地缓存 + Redis 后端”组合
必须分两层:Nginx 自身的 proxy_cache 或 fastcgi_cache 做第一道本地缓冲(内存+磁盘),Redis 仅作为该缓存未命中时的「上游数据源」。OpenResty 场景下,则用 Lua 控制缓存生命周期,手动管理 ngx.shared.DICT(共享内存字典) + lua-resty-redis 回源。
关键实操点:
- 用
proxy_cache_path定义共享内存区,例如proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=redis_cache:10m max_size=1g inactive=30m; - 在 location 中启用缓存:
proxy_cache redis_cache; proxy_cache_valid 200 302 10m;,并确保后端(如 Lua handler)返回标准Cache-Control头 - 对非法参数(如负 ID、超长字符串)在 Nginx 阶段就拦截:
if ($arg_id ~ "^-|[^0-9]") { return 400; },避免触达 Redis - OpenResty 中用
ngx.shared.dict存逻辑过期标记,只让首个请求触发 Redis 查询,其余请求读共享内存中的 stale 数据 + 异步刷新
如何防止穿透流量打穿 Redis 和 DB
单靠 Nginx 或 Redis 都无法根治穿透,必须协同拦截。核心是:不让无效请求走完整链路。
可立即生效的配置项:
- 在 Nginx 入口加正则过滤:
if ($args ~ "(id=)(-|\D)") { return 403; },干掉明显恶意参数 - Redis 层对空结果写
SET key "" EX 60(短 TTL 空值),但注意别用EX 1——太短会导致频繁回源,60~300 秒更稳妥 - 在 Lua 层集成布隆过滤器(
lua-resty-bloomfilter),初始化加载合法 ID 集合,请求先查布隆再决定是否查 Redis - 对高频 key(如商品详情)禁用自然过期,改用逻辑过期 + 定时任务刷新,避免击穿瞬间雪崩
最容易被忽略的一点:Nginx 的 proxy_cache 默认不缓存 4xx/5xx 响应,但穿透场景下大量 404 请求其实也该缓存(比如 proxy_cache_valid 404 1m)。否则攻击者反复刷不存在的 ID,照样能把 upstream 打满——因为每次都是“未命中缓存 → 查 Redis → 查 DB → 返回 404”,全程无收敛。











