香港初创企业网站访问变慢且可用内存偏低,如何沿请求链路排查缓存与进程占用?
一次访问香港初创企业网站,用户的请求通常会经过域名解析、接入层或缓存、网络连接、Web 服务器、应用进程,最后才到达数据库或其他后端组件。页面变慢与“可用内存偏低”同时出现,并不等于服务器内存已经被某一个进程全部吃光:也可能是缓存未命中导致请求集中回源,也可能是交换频繁、进程排队、应用进程泄漏,甚至只是 Linux 将空闲内存用于文件缓存后的正常显示。
排查时应沿请求链路由外到内进行:先确认慢的是全部请求还是动态请求,再核对缓存命中和各阶段耗时;随后检查网络连接,接着判断 available、交换活动和 OOM 记录,最后定位具体进程及其并发配置。修复后还要用相同请求分别验证缓存命中、缓存未命中和高并发状态,不能只看一次页面是否恢复。

先确认慢请求的范围
不要一开始就重启 Web 服务或清空所有缓存。先记录故障时间、受影响的 URL、访问是否间歇性失败,以及页面返回的是 502、503、504 还是正常响应但首字节很慢。
可以先对首页、一个静态资源和一个动态页面分别测试。下面的命令适用于 Linux 服务器,example.com 替换为实际域名:
curl -sS -o /dev/null -D - \
-w 'dns=%{time_namelookup}s connect=%{time_connect}s tls=%{time_appconnect}s starttransfer=%{time_starttransfer}s total=%{time_total}s\n' \
https://example.com/
重点看以下字段:
time_namelookup:域名解析耗时。time_connect:建立 TCP 连接耗时。time_appconnect:启用 HTTPS 时完成 TLS 握手的耗时。time_starttransfer:收到首字节前的耗时,通常最能反映缓存、排队和应用处理是否变慢。time_total:完整响应耗时,可能还包括传输大文件的时间。
如果静态文件很快,动态页面的 starttransfer 明显升高,问题更可能位于缓存未命中、应用进程或后端处理,而不是所有网络请求都变慢。如果所有 URL 的连接阶段都升高,应先检查网络路径和接入层。若页面偶尔出现 502 或 504,则要把进程被杀、上游超时和并发耗尽列为重点。
第一层:确认访问入口和缓存是否改变了请求量
缓存的作用不仅是减少响应时间,也能减少到达源站的请求数。当缓存突然大量失效、缓存键发生变化,或者登录 Cookie 让请求全部绕过缓存时,原本平稳的应用进程可能在短时间内同时处理大量请求,内存和交换区就会随之承压。
查看响应头和缓存状态
curl -sS -I https://example.com/
curl -sS -I https://example.com/static/app.css
重点观察:
Age、ETag、Cache-Control等缓存相关响应头;X-Cache、X-Cache-Status等由接入层或 Web 服务自行增加的状态头;- 是否出现
Set-Cookie、私有缓存控制或禁止缓存的指令; - 同一个 URL 连续请求时,响应头是否发生变化。
这些响应头不是所有环境都会默认提供。没有 X-Cache-Status 不能直接认定没有缓存,需要结合接入层日志、Nginx 配置或应用日志判断。
可以连续请求同一个页面,对比第一次和后续请求的 starttransfer:
for i in $(seq 1 5); do
curl -sS -o /dev/null \
-w "request=$i starttransfer=%{time_starttransfer}s total=%{time_total}s\n" \
https://example.com/
sleep 1
done
典型判断方式如下:
| 观察结果 | 更可能的含义 | 下一步 |
|---|---|---|
后续请求明显变快,且出现 Age 或 HIT 状态 | 缓存可以工作,首次请求可能回源 | 对比缓存未命中时的源站耗时 |
| 多次请求均未命中,应用耗时同步升高 | 缓存规则、缓存键或 Cookie 使请求持续回源 | 检查缓存绕过条件和回源日志 |
| 缓存命中但连接阶段仍慢 | 入口或网络连接存在问题 | 继续检查 DNS、TCP、TLS 和路径 |
| 缓存命中很快,但动态页面仍慢 | 慢点位于动态请求或应用进程 | 进入进程和上游处理检查 |
| 缓存刚清理后大量请求同时变慢 | 可能出现缓存重建和回源峰值 | 避免全量清理,先恢复热点缓存 |
需要区分三类缓存占用:
- Web 服务的磁盘缓存可能被 Linux 文件缓存映射到内存,这部分通常具有较强的可回收性。
- 应用内存缓存通常直接体现为某个进程的 RSS 增长,不能简单按“系统缓存”处理。
- 接入层缓存命中率下降,首先增加的是源站请求量,随后才可能表现为进程数量增加、内存升高和交换频繁。
因此,不要把“可用内存下降”和“缓存一定有问题”画等号,也不要在故障高峰直接执行全量缓存清理。清理后会造成更多回源请求,可能形成缓存击穿式的瞬时压力。
检查请求是否真的排队在源站
如果使用 Nginx 作为 Web 服务或反向代理,可以在已有访问日志中查找请求耗时、上游耗时和缓存状态。若现有日志没有这些字段,可以在计划维护窗口增加临时字段。
下面是 Nginx http 配置段中的示例,属于参考配置,不应直接覆盖整份配置文件:
log_format timing
'$remote_addr [$time_local] "$request" '
'status=$status request_time=$request_time '
'upstream_response_time=$upstream_response_time '
'upstream_status=$upstream_status '
'cache_status=$upstream_cache_status';
access_log /var/log/nginx/access_timing.log timing;
修改前应备份配置并确认日志目录权限,修改后先检查语法,再平滑加载:
sudo cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
sudo nginx -t
sudo systemctl reload nginx
如果 nginx -t 失败,不要继续加载;恢复备份前应先确认备份文件完整,再重新测试。临时日志可能增加磁盘写入,故障确认后可以撤回该配置。
分析日志时:
request_time高、upstream_response_time也高,通常表示应用或后端处理慢;request_time高、upstream_response_time较低,可能是响应发送、客户端连接或网络环节较慢;upstream_response_time为-,可能是静态文件、缓存命中,或者请求尚未到达上游;upstream_status大量出现 502、503、504,应结合应用进程、连接数和 OOM 日志继续判断。
第二层:检查网络路径,但不要用 ping 代替网页测试
网络诊断的作用是确定延迟增加发生在连接入口,还是发生在服务器收到请求之后。它不能单独证明应用正常或异常。
用 ping 观察基础连通性
ping -c 10 example.com
如果平均延迟升高或出现丢包,只能说明 ICMP 探测存在异常。某些设备会降低 ICMP 响应优先级,因此中间某一跳显示丢包、但最终目标正常,并不能直接证明该跳丢弃了网页请求。
用 traceroute 或 tracepath 观察路径变化
Linux 上可以先核验工具是否安装:
command -v tracepath
command -v traceroute
可用时执行:
tracepath example.com
或者:
traceroute -n -m 12 example.com
重点观察:
- 是否在某一段开始出现持续的延迟上升;
- 是否存在多次测试都出现的丢包或超时;
- 路径是否在故障前后发生变化;
- 最终目标是否能够完成探测。
traceroute 的中间星号并不必然表示真实转发丢包,因为路由设备可能不回复探测包。最终仍应以 curl 的 connect、TLS、starttransfer 和 total 分段耗时为准。
如果 connect 和 TLS 耗时升高,而源站日志中请求量和处理时间正常,优先检查接入层和网络路径。如果连接很快,但 starttransfer 很高,网络通常不是主要瓶颈,应回到缓存、队列和应用进程。
第三层:判断“可用内存偏低”是否是真正的内存压力
Linux 中 free 很低并不一定是故障。系统会把暂时不用的内存用于文件缓存,因此应重点看 available,再结合交换输入输出和内存压力指标。

记录内存、交换和压力指标
下面的命令适用于常见 Linux 发行版:
free -h
vmstat 1 5
swapon --show
cat /proc/pressure/memory
可以看到类似这样的示例结果:
total used free shared buff/cache available
Mem: 4.0Gi 3.1Gi 180Mi 120Mi 760Mi 620Mi
Swap: 2.0Gi 1.1Gi 920Mi
这组数值只能作为解释机制的示例,不代表某台实际服务器的监控结果。判断时按以下顺序进行:
available持续很低,说明系统可立即分配的内存余量不足;单次采样不足以证明故障。buff/cache较高但available仍充足,通常属于可回收缓存,不应直接清空。- Swap 已使用不等于当前正在发生严重交换。有些页面可能早已被换出,但近期没有再次访问。
vmstat中si和so持续出现较高数值,说明内存页面正在频繁换入换出,通常会直接拉高请求延迟。/proc/pressure/memory中some或full的avg10、avg60持续升高,说明进程因内存资源不足而等待。
没有统一适用于所有业务的硬阈值。作为预警参考,持续可用内存低于总内存的约 10%~20%,同时伴随交换活动或内存压力升高,就应立即检查进程和并发,而不是继续提高工作进程数。
检查是否发生 OOM
内核可能在系统或服务的内存上限耗尽时杀掉某个进程。适用于使用 systemd 的 Linux 系统的检查方式如下:
sudo journalctl -k --since "2 hours ago" | \
grep -Ei 'out of memory|oom|killed process'
也可以查看当前启动周期内的内核信息:
sudo dmesg -T | grep -Ei 'out of memory|oom|killed process'
出现类似 Killed process、Out of memory 的记录,说明已经发生过 OOM 处置。被杀的可能是应用进程,也可能是数据库或其他高占用服务。不要只根据“谁的 RSS 最大”来判断责任,还要结合被杀进程、发生时间和访问日志。
如果主机有足够 available,但某个服务仍被杀,需要检查服务或容器的内存上限。systemd 服务可以使用:
systemctl show -p MemoryCurrent -p MemoryMax
其中 应替换为实际服务名。先用下面的命令核验服务名称,避免猜测版本化服务名:
systemctl list-units --type=service --state=running
systemctl list-unit-files | grep -Ei 'nginx|php|fpm'
若 MemoryMax 设置得较低,问题可能是 cgroup 限额,而不是整台服务器物理内存不足。此时应调整服务资源规划或降低进程并发,并保留原配置以便回滚。
第四层:定位真正占用内存的进程
确认存在内存压力后,再区分是 Web 工作进程过多、单个进程异常膨胀、应用缓存过大,还是后端服务占用过高。
先按 RSS 排序
ps -eo pid,ppid,user,comm,%mem,rss,%cpu --sort=-rss | head -n 20
rss 通常以 KiB 显示,代表进程当前驻留在物理内存中的部分;VSZ 是虚拟地址空间,不能直接当作实际占用。多个工作进程可能共享代码页,因此把每个 RSS 简单相加会有重复计算,但它仍适合用来发现异常进程。
可以持续观察进程数量和内存变化:
vmstat 1 5
ps -C nginx -o pid,ppid,comm,%mem,rss,%cpu --sort=-rss
如果系统中的 Nginx 服务名或进程名不同,应先通过 ps -ef | grep 或服务清单确认,避免使用错误名称。
重点看三种形态
一是进程数量过多。 应用单进程占用并不高,但工作进程数量快速增加,同样会耗尽内存。常见原因包括并发上限过高、请求阻塞、后端响应慢导致连接长期占用,以及缓存未命中后大量请求同时回源。
二是单个进程持续增长。 如果某个应用进程的 RSS 随时间不断上升,重启后暂时恢复,之后再次增长,可能存在内存泄漏、缓存没有淘汰策略或异常请求积压。短期可以安排受控的进程回收,长期仍需检查代码、扩展和缓存生命周期。
三是进程内存稳定但总内存不足。 这时应把所有主要服务放在一起看,包括 Web 服务、应用运行时、数据库、日志处理和监控进程。只盯着 Web 进程,可能会漏掉真正占用内存的后端组件。
如果已安装 sysstat,还可以用 pidstat 观察进程随时间的变化:
pidstat -r -u -p ALL 1 5
minflt/s、majflt/s、CPU 和进程状态需要结合 vmstat、访问日志一起看。单项指标升高不一定意味着故障,但某个进程在请求变慢时 RSS、CPU 和缺页活动同时升高,说明它很可能正在成为瓶颈。
第五层:把进程占用与应用并发联系起来
应用运行时的并发参数不能只按 CPU 核数设置,还必须考虑每个工作进程的实际内存。
以 PHP-FPM 或类似进程池为例,常见影响因素包括:
- 最大工作进程数;
- 每个工作进程的平均 RSS 和峰值 RSS;
- 请求执行时间;
- 是否存在慢查询或外部调用;
- 是否设置了进程回收上限;
- 进程池是否存在空闲、忙碌、排队状态。
可以用下面的估算思路规划内存:
可分配给应用进程的内存 ≈ 总内存 − 系统预留 − Web 服务 − 数据库及其他服务 − 安全余量
例如一台 4 GiB 服务器,系统和基础服务约占 700 MiB,后端服务约占 900 MiB,Web 层和监控约占 300 MiB,预留 700 MiB 后,应用进程池可使用的空间约为 1.4 GiB。若单个应用进程高峰 RSS 约 90 MiB,理论上不能简单地把 max_children 设置为 15,因为进程共享页、临时内存、请求峰值和其他服务都会产生波动。实际值应根据连续采样后的峰值 RSS 留出余量。
进程池参数的调整顺序建议如下:
- 先记录当前最大进程数、忙碌进程数和请求失败时间。
- 测量单个进程的平均及峰值 RSS,而不是只看空闲时内存。
- 在保留系统和后端余量的前提下,降低过高的并发上限。
- 观察排队是否增加;如果排队明显增加,应继续查找慢请求原因,而不是盲目把并发调回去。
- 对疑似泄漏的进程,可以谨慎设置请求数上限,让进程处理一定数量请求后平滑回收,但这只是缓解措施,不能替代代码排查。
修改进程池或 Web 服务配置前,先备份原文件,使用对应软件的语法检查命令,再采用平滑 reload。不要在高峰期直接 stop 服务;如果新配置导致错误,应使用备份恢复,重新通过语法检查后再 reload。具体服务名、配置路径和参数名称可能随发行版及软件版本变化,先用服务状态和进程启动参数核验,不要照搬未经确认的路径。
根据结果选择修复顺序
不同现象对应的处理优先级不同:
| 主要证据 | 优先处理方向 | 不宜立即做的事 |
|---|---|---|
| 缓存 MISS 增多,源站进程和回源耗时上升 | 检查缓存键、Cookie 绕过条件和失效策略 | 不要直接全量清缓存 |
available 低,si/so 持续升高 | 降低进程并发、处理慢请求并恢复内存余量 | 不要继续增加工作进程 |
| 出现 OOM 记录,某类进程被杀 | 检查服务内存上限、单进程峰值和进程数量 | 不要只重启被杀服务后结束排查 |
| 单个进程 RSS 持续增长 | 检查内存泄漏和应用缓存淘汰策略 | 不要把频繁重启当成长期修复 |
connect、TLS 耗时升高,源站处理正常 | 排查接入层和网络路径 | 不要先改应用并发 |
starttransfer 高且上游耗时高 | 查应用队列、后端处理和数据库等待 | 不要只增加缓存容量 |
增加 Swap 可以降低部分突发 OOM 风险,但不能把它当作性能修复。若交换活动已经频繁,继续依赖 Swap 往往会让请求延迟更加不稳定。扩充内存也应建立在进程峰值、缓存占用、并发和 OOM 记录之上,否则可能只是延后同类问题。
修复后的验证顺序
验证必须覆盖“缓存命中”和“缓存未命中”两类请求,同时保留资源监控。建议按以下顺序进行:
- 先测静态资源。
确认 DNS、连接和静态响应耗时恢复,排除网络或接入层仍然存在的问题。
- 再测缓存命中页面。
连续请求同一 URL,确认缓存状态稳定、starttransfer 降低,源站应用进程没有同步增加。
- 再测动态或缓存绕过页面。
使用实际业务请求验证应用进程是否仍然排队,观察 upstream_response_time、502/503/504 和进程数。
- 持续观察一个业务高峰周期。
至少同时记录 free -h、vmstat、Swap 活动、OOM 日志和主要进程 RSS。不要只在重启后的几分钟内判断修复成功。
- 比较修复前后的同口径数据。
关注首字节耗时、完整响应耗时、缓存命中率、应用进程峰值、可用内存最低点和错误率。修复成功的表现通常是:缓存命中稳定,未命中请求不会造成进程数失控,available 不再持续下探,交换输入输出恢复平稳,且没有新增 OOM 记录。
如果复测时缓存命中恢复但动态页面仍慢,瓶颈在应用处理链路;如果应用耗时恢复但所有请求连接仍慢,应继续看网络入口;如果延迟下降却出现可用内存快速减少,则要继续追踪进程 RSS 和缓存增长趋势。这样才能判断香港 Web 服务器是否真正拥有足够的内存余量,而不是依靠一次重启或一次缓存清理暂时掩盖问题。