1023 字
5 分钟
提升 Web 性能:基于 Nginx 和 Memcached 的页面缓存方案

1. 为什么选择 Nginx + Memcached?#

在传统的 Web 架构中,即使使用了后端缓存(如在 PHP 或 Python 代码中调用 Redis),请求依然需要经过 Nginx -> 后端应用解析器 -> 缓存系统 这一漫长的链路。

通过使用 Nginx 的 ngx_http_memcached_module 模块,我们可以实现 Nginx -> Memcached 的直接通信。当缓存命中时,请求无需触达后端应用逻辑,从而实现“零开销”分发动态内容,单机并发处理能力通常可提升数倍甚至数十倍。


2. 工作原理图解#

  1. 请求拦截:Nginx 接收请求,根据预设规则生成缓存键(通常基于 URL)。
  2. 直接提取:Nginx 发起向 Memcached 的异步查询。
  3. 缓存命中 (Hit):数据直接由 Nginx 返回客户端,耗时极低。
  4. 缓存未命中 (Miss):Nginx 通过 error_pagetry_files 将请求回源给后端应用(如 FastCGI),后端处理完逻辑后将结果回写至 Memcached。

3. 生产环境配置指南#

第一步:配置 Nginx 缓存逻辑#

在 Nginx 配置文件中,我们需要定义上游服务器地址,并精细化处理请求头,以确保缓存能够正确返回。

# 定义 Memcached 集群
upstream memcached_backend {
server 10.1.240.166:11211;
keepalive 32; # 保持长连接,提升性能
}
server {
listen 80;
server_name example.com;
location / {
# 1. 定义缓存 Key:这里使用完整请求 URI
set $memcached_key "$request_uri";
# 2. 尝试从缓存中获取
memcached_pass memcached_backend;
# 3. 设置默认类型及响应头以便调试
default_type text/html;
add_header X-Cache-Status HIT;
add_header X-Memcached-Key $memcached_key;
# 4. 如果发生错误(404/502/504),则跳转到内部命名的 location @fallback
error_page 404 502 504 = @fallback;
}
# 后端逻辑处理与缓存写入
location @fallback {
add_header X-Cache-Status MISS;
proxy_pass http://your_backend_server; # 或者 fastcgi_pass
}
}

第二步:后端应用写入缓存(以 PHP 为例)#

后端不仅要负责处理业务逻辑,还要在生成内容后,将结果通过 Key-Value 形式存入 Memcached。

<?php
$m = new Memcached();
$m->addServer('10.1.240.166', 11211);
// 建议使用当前请求的 URI 作为 Key
$cacheKey = $_SERVER['REQUEST_URI'];
$content = $m->get($cacheKey);
if ($content) {
echo $content;
} else {
// 开启缓冲区捕获页面输出
ob_start();
// 执行复杂的业务逻辑、数据库查询等
render_complex_page();
$output = ob_get_contents();
ob_end_flush();
// 存入缓存,设置过期时间(例如 3600 秒)
$m->set($cacheKey, $output, 3600);
}

4. 关键避坑与进阶建议#

4.1 内容类型(Content-Type)丢失问题#

标准的 ngx_http_memcached_module 并不存储 HTTP 头信息。如果你的页面有多种 MIME 类型,直接读取可能会导致浏览器无法正确识别 HTML 或图片。

  • 对策:在后端存入缓存时,额外存储一个头信息 Key,或者在 Nginx 中根据扩展名固定 default_type

4.2 缓存压缩(Gzip)#

为了节省内存,后端通常会将内容压缩后存入 Memcached。

  • 注意:如果你在后端压缩了内容,必须在 Nginx 配置中手动添加 gunzip on;(需要安装 ngx_http_gunzip_module),或者在返回时强制加上 add_header Content-Encoding gzip;

4.3 缓存失效(Cache Invalidation)#

Memcached 不支持模糊删除。如果某个新闻分类下的所有页面都变了,你很难一次性清除所有相关的 Key。

  • 进阶方案:引入版本号机制(Namespace),在 Key 中加入版本号前缀,更新时只需修改版本号即可让旧缓存全部失效。

5. 方案对比:Memcached vs Redis#

特性MemcachedRedis
性能极高(简单 KV 协议,系统开销更小)高(功能丰富,略有系统开销)
持久化不支持支持
Nginx 支持原生模块支持,配置简单需安装 srcache-nginx-module 等第三方模块
数据类型仅字符串字符串、哈希、列表、集合等

6. 总结#

结合 Nginx 与 Memcached 的页面缓存方案是 “用空间换时间” 的经典实践。它尤其适用于:

  1. 高频访问的 CMS 页面(如新闻首页、公告)。
  2. API 查询结果缓存
  3. 计算代价昂贵的报表页面

虽然现在 Redis 非常流行,但在追求极致的简单 KV 查询性能时,Nginx + Memcached 的原生组合依然是运维利器。

提升 Web 性能:基于 Nginx 和 Memcached 的页面缓存方案
https://sw.rscclub.website/posts/nginx-memcache/
作者
杨月昌
发布于
2020-12-16
许可协议
CC BY-NC-SA 4.0