Nginx实现本地缓存查询

一、前言:什么是"本地缓存查询"?

在微服务架构中,我们经常遇到这样的场景:

  • 用户频繁查询商品详情(GET /api/product/123
  • 客户端轮询配置接口(GET /api/config/app
  • 第三方调用只读数据接口(如城市列表、字典项)

这些请求具有高读低写、结果稳定、重复率高的特点。

❌ 传统做法:每次请求都穿透到后端 → 数据库压力大、响应慢

Nginx 本地缓存查询 :将后端返回的 JSON 响应缓存在 Nginx 本地磁盘,后续相同查询直接返回缓存!

本文将教你如何用 Nginx 的 proxy_cache 模块,实现高效的本地缓存查询能力


二、核心机制:proxy_cache 如何工作?

💡 关键优势

  • 零代码改造:后端无需任何改动
  • 毫秒级响应:缓存命中时 RT < 1ms
  • 自动过期:按配置时间自动清理
  • 磁盘持久化:Nginx 重启后缓存仍有效(除非超时)

三、基础配置:5 步开启本地缓存查询

步骤 1:定义缓存区域(http 块)

Lua 复制代码
http {
    # 定义用于查询缓存的区域
    proxy_cache_path /var/cache/nginx/query 
        levels=1:2                # 两级目录防 inode 耗尽
        keys_zone=query_cache:100m  # 共享内存区(100MB ≈ 80万 key)
        inactive=10m              # 10分钟未访问则删除
        max_size=2g               # 最大缓存 2GB
        use_temp_path=off;        # 关键!避免临时文件拷贝性能损耗

    # 创建目录并授权
    # mkdir -p /var/cache/nginx/query && chown nginx:nginx /var/cache/nginx/query
}

步骤 2:在查询接口启用缓存

Lua 复制代码
server {
    listen 80;
    server_name api.example.com;

    # 商品查询接口(只读、高频)
    location /api/product/ {
        # 启用缓存
        proxy_cache query_cache;
        
        # 自定义缓存 key(确保唯一性)
        proxy_cache_key "$host|$request_uri";
        
        # 仅缓存 200 成功响应,有效期 5 分钟
        proxy_cache_valid 200 5m;
        
        # 忽略后端 Cache-Control 头(由 Nginx 统一控制)
        proxy_ignore_headers Cache-Control Expires;
        
        # 透传真实 IP
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://product_service;
    }

    # 字典查询接口(几乎不变)
    location /api/dict/ {
        proxy_cache query_cache;
        proxy_cache_valid 200 1h;  # 缓存 1 小时
        proxy_pass http://dict_service;
    }
}

步骤 3:定义上游服务

Lua 复制代码
upstream product_service {
    server 10.0.0.10:8080;
}

upstream dict_service {
    server 10.0.0.20:8080;
}

四、高级技巧:精准控制缓存查询行为

4.1 添加缓存状态头(用于调试)

Lua 复制代码
# 在 location 中添加
add_header X-Cache-Status $upstream_cache_status always;

# 返回值说明:
# HIT    → 命中缓存
# MISS   → 未命中(已回源)
# BYPASS → 跳过缓存(如 POST 请求)

4.2 强制跳过缓存(调试用)

Lua 复制代码
# 请求带 ?debug=1 时不走缓存
if ($arg_debug = "1") {
    set $nocache "1";
}
proxy_cache_bypass $nocache;
proxy_no_cache $nocache;

4.3 防止缓存穿透(恶意查询)

Lua 复制代码
# 对不存在的 ID 返回 404 并缓存 1 分钟
proxy_cache_valid 404 1m;

# 或限制 ID 格式(正则匹配)
location ~ ^/api/product/(\d+)$ {
    proxy_cache query_cache;
    proxy_cache_valid 200 5m;
    proxy_pass http://product_service;
}

五、生产级最佳实践

5.1 缓存托底:后端挂了也能查

Lua 复制代码
location /api/campaign/ {
    proxy_cache query_cache;
    proxy_cache_valid 200 30s;
    
    # 关键!后端错误时使用过期缓存
    proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
    
    proxy_pass http://campaign_service;
}

效果 :即使后端宕机,用户仍能查看最近一次活动数据

5.2 性能优化建议

项目 推荐值 说明
存储介质 SSD 磁盘 避免 HDD I/O 瓶颈
inactive 5~30 分钟 根据数据更新频率调整
max_size ≤ 磁盘 80% 防止占满磁盘导致服务异常
keys_zone ≥ 50MB 高 QPS 场景需更大内存

5.3 清理缓存(手动刷新)

bash 复制代码
# 示例:清除商品 123 的缓存
KEY="api.example.com|/api/product/123"
HASH=$(echo -n "$KEY" | openssl md5 | cut -d' ' -f2)
LEVEL1=${HASH: -1}
LEVEL2=${HASH: -3:2}
rm -f /var/cache/nginx/query/$LEVEL1/$LEVEL2/$HASH
# 无需 reload,下次请求自动重建

六、验证缓存是否生效

6.1 通过响应头判断

bash 复制代码
curl -I "http://api.example.com/api/product/123"

首次请求

复制代码
HTTP/1.1 200 OK
X-Cache-Status: MISS
...

后续请求

复制代码
HTTP/1.1 200 OK
X-Cache-Status: HIT
...

6.2 查看缓存文件

bash 复制代码
# 查看缓存目录
ls /var/cache/nginx/query/a/bc/
# 输出:abcdef1234567890... (MD5 哈希值)

七、常见问题排查

问题 原因 解决方案
始终 MISS 后端返回 Cache-Control: no-cache 添加 proxy_ignore_headers Cache-Control;
缓存未过期 inactive 时间未到 检查 proxy_cache_validinactive 区别
磁盘空间不足 未设置 max_size 设置合理的 max_size 并监控磁盘
中文乱码 后端未指定 UTF-8 确保后端返回 Content-Type: application/json; charset=utf-8

八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
刘~浪地球2 小时前
数据库与缓存--Redis 集群架构与优化
数据库·redis·缓存
大佐不会说日语~2 小时前
Spring AI Alibaba 的 Function Calling 使用 @Tool 调用中,无法获取用户ID踩坑记录
java·人工智能·spring boot·spring·alibaba·function
IT枫斗者2 小时前
AI Agent 设计模式全景解析:从单体智能到分布式协作的架构演进
人工智能·redis·分布式·算法·spring·缓存·设计模式
一个有温度的技术博主3 小时前
Redis缓存预热:解决服务冷启动的“数据库杀手”问题
数据库·redis·缓存
ZzzZZzzzZZZzzzz…12 小时前
Nginx 平滑升级:从 1.26.3 到 1.28.0,用户无感知
linux·运维·nginx·平滑升级·nginx1.26.3·nginx1.28.0
xiaoye370813 小时前
Spring 中高级面试题
spring·面试
花花鱼13 小时前
Spring Security 与 Spring MVC
java·spring·mvc
还在忙碌的吴小二15 小时前
Harness 最佳实践:Java Spring Boot 项目落地 OpenSpec + Claude Code
java·开发语言·spring boot·后端·spring
老神在在00117 小时前
Spring Boot 全局异常处理器(GlobalExceptionHandler)
spring boot·spring·java-ee·状态模式·