Debian Nginx访问变慢如何判断资源瓶颈?联动CPU、内存与磁盘I/O
单看 CPU、内存或磁盘中的任何一个指标,都可能把 Debian Nginx 的访问变慢归因错误。更可靠的判断方式是建立同一时间窗口,把请求耗时、错误率、CPU 运行队列、内存回收、磁盘延迟、网络队列以及上游响应时间放在一起观察:CPU 使用率高且运行队列持续堆积,才更像 CPU 瓶颈;内存可用量下降并伴随换入换出,才支持内存不足;磁盘 await、队列和 iowait 同时上升,才应优先检查磁盘 I/O。若主机资源正常但 $upstream_response_time 持续升高,则应转向应用或数据库,而不是继续调整 Nginx worker 数量。
实际操作中,可以先在访问变慢的 5~10 分钟内记录 Nginx 请求时间,再同步采集 vmstat、iostat、sar、ss 和进程数据。之后按照“请求耗时变化 → 系统指标联动 → 排除替代解释 → 得出瓶颈类型 → 复测”的顺序判断。下面的数值和监控结果均为解释机制用的典型示例,不代表某台服务器的实测结果。

先建立统一的观察窗口
记录请求端到端耗时
如果只执行一次 curl,结果可能受到网络抖动、缓存命中和请求内容差异影响。应选定一个稳定的 URL、请求方法和测试参数,在业务允许的范围内重复观察,不要在生产高峰随意进行高并发压测。
curl -sS -o /dev/null \
-w 'code=%{http_code} dns=%{time_namelookup} connect=%{time_connect} start=%{time_starttransfer} total=%{time_total} size=%{size_download}\n' \
--max-time 15 \
https://example.com/health
重点关注以下变化:
time_starttransfer较高,说明等待服务端开始返回数据的时间变长,应用、数据库、上游连接或服务器排队都可能参与其中。time_total较高但time_starttransfer正常,可能是响应体传输慢、客户端接收慢或网络拥塞。- HTTP 状态码从 2xx 变为 499、502、504 或 5xx 时,应将错误率和耗时放在同一窗口观察。
- 访问单个 URL 变慢,不一定表示整台服务器资源不足,也可能只是该接口的查询、锁等待或业务逻辑发生变化。
同时采集系统指标
Debian 常见的 vmstat、iostat 和 sar 分别用于观察运行队列与内存、块设备 I/O、网络及 TCP 状态。如果系统没有 iostat 或 sar,可以安装 sysstat;安装前应确认服务器允许更新软件包索引,生产环境最好在维护窗口执行。
sudo apt-get update
sudo apt-get install -y sysstat
故障发生时,可以先进行一组短时采样:
date '+%F %T'
uptime
free -h
vmstat 1 10
iostat -xz 1 10
sar -n DEV,TCP,ETCP 1 10
ss -s
ps -eo pid,ppid,stat,comm,%cpu,%mem --sort=-%cpu | head -n 15
短时采样只能帮助确认现场。若访问变慢持续数分钟,应让采样覆盖完整故障窗口,并记下每条命令的时间。vmstat 中的 r、b、si、so、wa,以及 iostat 中的 await、队列、%util,需要和 Nginx 日志中的请求时间对齐,而不能拿不同时间段的数据进行比较。
先把 Nginx 请求时间拆开
使用上游时间区分 Nginx、应用和客户端
如果 Nginx 当前日志没有记录请求耗时,可以临时增加一套性能日志。配置变更前先备份 Nginx 配置;额外日志会增加写盘量,在磁盘已经接近饱和时不宜立即启用。
在 /etc/nginx/nginx.conf 的 http 配置块中增加唯一名称的日志格式:
log_format perf
'$time_iso8601 request="$request" '
'status=$status upstream_status=$upstream_status '
'request_time=$request_time '
'upstream_connect_time=$upstream_connect_time '
'upstream_header_time=$upstream_header_time '
'upstream_response_time=$upstream_response_time '
'bytes_sent=$bytes_sent';
access_log /var/log/nginx/perf_access.log perf;
修改后先检查语法,再平滑加载:
sudo nginx -t
sudo systemctl reload nginx
nginx -t 失败时不会执行 reload,应根据报错修正配置。若新日志导致写盘压力增加,或需要恢复原有日志格式,应使用修改前的备份文件恢复,重新执行 nginx -t,确认通过后再 reload。不要直接覆盖配置而不保留回滚副本。
这些字段可以这样理解:
| 指标 | 主要含义 | 适合验证的问题 |
|---|---|---|
request_time | Nginx 处理该请求的总耗时 | 用户感知的服务端请求时间 |
upstream_connect_time | 与上游建立连接所用时间 | 上游连接、网络、连接队列是否异常 |
upstream_header_time | 从连接上游到收到响应头的时间 | 应用处理、数据库查询、锁等待是否变慢 |
upstream_response_time | 从连接上游到接收完整响应的时间 | 上游整体处理和响应传输时间 |
status | Nginx 返回给客户端的状态码 | 499、5xx 是否随延迟增加 |
upstream_status | 上游返回的状态码 | 应用服务是否主动报错 |
例如,request_time 从 0.2 秒升到 3 秒,同时 upstream_response_time 也接近 2.8 秒,通常应先调查应用或数据库。若 upstream_response_time 只有 0.1 秒,但 request_time 达到 3 秒,则要检查客户端接收、网络发送队列、响应体大小以及 Nginx 所在主机的 I/O。
判断 CPU 是否真的成为瓶颈
CPU 瓶颈不是“CPU 使用率超过某个数字”这么简单,而是请求变慢时,处理器繁忙、运行队列堆积和相关进程耗时同时出现。
在 vmstat 中:
us表示用户态 CPU 时间,应用计算、脚本执行、压缩等可能使它升高。sy表示内核态 CPU 时间,网络处理、系统调用和 I/O 相关内核工作可能使它升高。id是空闲比例。r是等待运行的任务数,持续高于可用 CPU 核数时,说明存在运行队列压力。wa是 CPU 等待 I/O 的时间,不能简单当作 CPU 计算瓶颈。
典型的 CPU 瓶颈组合是:
request_time 持续升高
vmstat r 长时间接近或超过 vCPU 数量
vmstat us+sy 较高,id 较低
vmstat wa 不高
nginx 或应用进程 在 ps/pidstat 中持续占用较多 CPU
例如,8 vCPU 服务器在访问变慢期间持续出现 r=10、id=5%,同时 Nginx worker 或应用进程的 CPU 使用率很高,这比单独看到一次 95% CPU 更能支持“CPU 排队”的判断。
但以下情况不能直接归因于 CPU:
load average很高,而r并不高、wa很高。Linux 的 load average 也会包含处于不可中断 I/O 等待的任务。- CPU 总使用率不高,但某个单线程应用已经占满一个核心。应查看单进程或单线程分布,而不是只看总百分比。
- Nginx CPU 较低,但上游应用响应时间很高。此时 Nginx 可能只是在等待应用返回。
- CPU 短暂冲高,但请求耗时和错误率没有同步变化。短时任务不一定构成用户可感知的瓶颈。
虚拟机环境还可以关注 vmstat 的 st。如果 st 持续升高,说明虚拟 CPU 受到宿主机调度影响;这属于运行环境的 CPU 争用,不能通过单纯调整 Nginx 配置解决。
判断内存是否导致访问变慢
Debian 上使用 free -h 时,不要只看 free 一列。Linux 会将空闲内存用于文件缓存,因此 free 较小并不等于内存不足,应重点看 available,并结合换入换出和内存压力。
free -h
swapon --show
cat /proc/pressure/memory
vmstat 1 10
更有说服力的内存瓶颈组合通常包括:
available持续下降,而不是瞬时变小;vmstat的si、so持续有数据,表明发生交换分入或分出;memoryPSI 中的some或full压力持续增加;- 请求耗时、磁盘读写延迟和进程响应时间同步变差;
- 内核日志出现 OOM 或进程被终止记录。
参考判断可以是:如果可用内存降到总内存的约 10% 以下,同时 si/so 持续增长并伴随响应时间上升,应优先处理内存压力。但这个比例只是报警起点,不能替代现场验证。不同业务的缓存规模、连接数和内存峰值差异很大。
需要排除几个常见误判:
free很低但available仍充足、没有 swap 活动,通常只是文件缓存较多。- swap 已启用不代表当前一定存在内存瓶颈,关键是观察
si/so是否在故障窗口持续增长。 - 主机内存充足,但 Nginx 或应用受到 systemd、容器或 cgroup 内存上限限制,进程仍可能被迫回收或 OOM。此时要同时检查服务或容器的资源限制。
- 内存回收引起的磁盘读写会表现为
wa和磁盘await上升,因此内存与磁盘问题可能同时出现,不能只归类为 I/O。
如果内存指标正常,swap 没有活动,PSI 也没有明显压力,那么访问变慢时应暂时降低“内存不足”的优先级,转而检查 CPU、磁盘、网络和上游响应。
判断磁盘 I/O 是否拖慢 Nginx
磁盘问题通常要同时观察设备延迟、队列、利用率和请求时间。执行:
iostat -xz 1 10
vmstat 1 10
df -h
df -i
iostat -xz 中不同版本的列名可能略有差异,常见关注项包括:
await:读写请求从提交到完成的平均等待时间,单位通常为毫秒。r_await、w_await:读请求和写请求的等待时间。aqu-sz或avgqu-sz:设备请求队列长度。%util:设备忙碌比例。rkB/s、wkB/s:实际读写速率。
较强的磁盘瓶颈证据通常是:请求变慢的同一时间段内,wa 上升,设备 await 从平时的个位数毫秒增加到几十甚至更高,队列变长,%util 长时间接近设备饱和,同时 Nginx 访问日志中的耗时也上升。
需要注意,%util 在不同存储设备上的解释并不完全相同。高速设备可能在较高吞吐下仍有较低延迟,机械盘或共享存储则可能较早出现排队。因此不要用“利用率达到某个数字”单独下结论,必须同时看 await、队列和请求时间。
Nginx 访问变慢与磁盘相关的常见场景包括:
- 静态文件读取延迟升高;
- access log、error log 或其他业务日志写入堆积;
- 内存不足引发页面换入换出;
- 应用或数据库在同一设备上进行大量读写;
- 文件系统空间或 inode 用尽,导致写日志、创建临时文件失败。
如果只有磁盘 wkB/s 突然上升,而 Nginx 请求时间没有变化,可能是后台任务或其他进程造成的独立 I/O。可以结合进程级数据进一步确认:
pidstat -d 1 10
如果 Nginx 日志记录的 request_time 很高,但 upstream_response_time 很低,同时 Nginx worker 的磁盘写入明显增加,应检查日志写入和客户端响应过程;如果上游时间也很高,则磁盘可能位于应用或数据库所在路径,而不一定是 Nginx 自身。
排除网络和连接队列问题
网络瓶颈不能只看带宽。服务器网卡的收发速率、丢包和错误、TCP 重传、连接发送队列以及客户端接收速度,都可能造成访问变慢。
ip -br link
ip -s link show dev eth0
sar -n DEV,TCP,ETCP 1 10
ss -s
ss -lnt
其中 eth0 只是示例,应替换为 ip -br link 显示的实际接口名称。
可以重点观察:
rx、tx速率是否接近已知的接口容量;- 网卡错误、丢包计数是否在故障窗口增长;
- TCP 重传是否增加;
- 监听 socket 的
Recv-Q是否长期堆积; - 已建立连接的
Send-Q是否持续增长; - Nginx 的
request_time是否明显高于upstream_response_time。
网络瓶颈的一个典型表现是:上游在 100 毫秒内已经生成响应,但 Nginx 到客户端的总耗时达到数秒,期间发送队列、重传或网卡丢包增加。这时继续优化应用查询可能不会立即改善用户体验。
也要区分服务器网络和客户端网络:
- 如果只有个别客户端慢,服务器整体请求时间、错误率和网卡指标正常,可能是客户端链路或局部网络问题。
- 如果大量客户端同时变慢,服务器发送队列、重传和错误率同步增加,才更支持服务端网络或出口拥塞。
- 如果
Recv-Q持续增长,可能是应用处理不及时、连接接受能力不足或上游排队;它本身不能单独证明网卡带宽不足。
对于 HTTP 状态码,499 通常表示客户端在 Nginx 返回前主动关闭连接。它可能由客户端超时引起,也可能是服务端响应太慢导致客户端放弃,因此应与 request_time、网络重传和上游耗时一起判断,而不是直接当成 Nginx 故障。
区分应用瓶颈与数据库瓶颈
当 Nginx 作为反向代理时,应用和数据库问题往往都会表现为“上游响应慢”。因此,Nginx 能够确认的是上游延迟,不能仅凭本机日志直接证明数据库是根因。
可以先按以下模式缩小范围:
| 现象组合 | 更可能的方向 | 下一步验证 |
|---|---|---|
upstream_connect_time 升高 | 上游连接建立、网络、连接接受队列 | 查看应用监听状态、连接数和 TCP 队列 |
upstream_header_time 升高 | 应用处理、数据库查询或锁等待 | 查看应用处理日志、数据库活动会话和慢查询 |
upstream_header_time 正常,但完整 upstream_response_time 较高 | 上游响应体生成或传输较慢 | 检查响应大小、应用输出和网络发送 |
| Nginx CPU、内存、磁盘均正常,上游时间持续升高 | 应用或数据库优先级较高 | 分别查看应用队列、连接池、数据库查询和锁 |
| 只有某个接口变慢,静态文件和其他接口正常 | 路由逻辑、特定查询或接口依赖 | 对比该接口的参数、缓存和 SQL 耗时 |
| 5xx、502、504 与上游耗时同时增加 | 上游过载、崩溃或超时 | 查看应用错误日志、进程状态和上游服务可用性 |
应用侧应关注请求排队时间、工作线程或协程数量、连接池等待、接口处理耗时和错误日志。数据库侧则需要查看活动连接数、连接池等待、慢查询、锁等待、事务堆积及数据库自身的 CPU、内存和磁盘指标。
如果只能看到 Nginx 日志,比较稳妥的表述是“上游服务响应变慢”,不要直接写成“数据库导致”。只有当应用日志显示请求在等待数据库,且数据库侧同时出现慢查询、锁等待或资源压力时,才能把判断进一步收敛到数据库。
用联动数据形成判断
下面是一组模拟监控结果,用于说明如何排除替代解释。
示例一:不是 CPU,而是应用或数据库等待
在 8 vCPU 主机上,某接口的指标从正常到异常变化如下:

| 指标 | 正常窗口 | 变慢窗口 |
|---|---|---|
request_time | 0.22 秒 | 2.90 秒 |
upstream_response_time | 0.16 秒 | 2.75 秒 |
vmstat r | 1~3 | 2~4 |
CPU id | 65%~75% | 55%~68% |
CPU wa | 2%~5% | 3%~6% |
磁盘 await | 3~6 毫秒 | 4~8 毫秒 |
swap si/so | 0 | 0 |
请求总耗时和上游耗时几乎同步增加,但运行队列没有超过 CPU 数量,磁盘延迟、换入换出也正常。此时 CPU、内存和磁盘都缺乏支持证据,应优先检查应用线程、连接池、接口内部查询和数据库等待。
示例二:更像磁盘 I/O
另一时间窗口中,数据表现为:
request_time从 0.3 秒升至 2.6 秒;upstream_response_time仍在 0.2 秒左右;vmstat wa从 4% 升至 35%;- 某数据盘
await从 5 毫秒升至 90 毫秒; aqu-sz持续增长,%util长时间接近高位;- Nginx 或其他进程的日志写入量明显增加。
此时请求主要卡在 Nginx 到客户端的处理或本地 I/O,不能仅凭应用响应很快就认为整条链路正常。应进一步确认日志、静态文件、临时目录或同一设备上的其他进程是否制造了队列。
示例三:更像网络或客户端接收问题
如果:
upstream_response_time约为 0.1 秒;request_time升至 3 秒;- CPU、内存和磁盘指标基本稳定;
Send-Q增长,TCP 重传计数同步增加;- 大响应接口比小响应接口更明显;
则网络发送、链路丢包或客户端接收速度应优先排查。若只有一小部分客户端出现该现象,服务器整体资源正常,则不能据此认定 Nginx 主机网络已经饱和。
一套低风险的现场判断步骤
- 确认现象范围。 记录变慢的 URL、请求方法、状态码、响应大小和发生时间,区分是所有请求变慢,还是某个接口、某种响应体或某批客户端变慢。
- 建立时间基线。 在同一时间窗口保存
curl耗时、Nginx access log、vmstat、iostat、sar和ss输出,不要拿故障后的资源数据解释故障前的延迟。 - 先看请求时间拆分。 比较
request_time和upstream_response_time,再看upstream_connect_time、upstream_header_time,确定等待发生在 Nginx、连接、应用处理还是响应传输阶段。 - 检查 CPU 与运行队列。 同时观察
us、sy、id、wa和r,确认是否为持续排队,而不是一次性 CPU 峰值。 - 检查内存与交换。 关注
available、si/so和 PSI;仅凭free较小不要下结论。 - 检查磁盘延迟和队列。 将
await、%util、队列长度与wa、日志写入和请求耗时对齐。 - 检查网络队列和重传。 查看接口错误、TCP 重传、监听队列及已建立连接的
Send-Q,判断是否存在服务器侧传输阻塞。 - 进入应用和数据库验证。 只有当上游耗时升高且主机基础资源无法解释时,才把排查重点转向应用队列、连接池、慢查询和锁等待。
- 一次只改变一个因素后复测。 例如先暂停一个非必要的日志写入任务或处理明确的资源异常,再用相同 URL、相同请求参数和相近并发量复测。若同时修改 Nginx、应用和数据库配置,就无法判断哪项变化产生了效果。
常见误判和适用边界
单个阈值不能替代关联判断
CPU 90%、磁盘 %util 90% 或内存使用率 90%都只能作为告警线索。短时峰值可能是正常任务,持续时间、队列长度、请求耗时和错误率的同步变化才决定它是否构成用户侧瓶颈。
load average 不等于 CPU 使用率
load average 高可能来自 CPU 排队,也可能来自等待磁盘的任务。需要用 vmstat 的 r 和 wa 分开观察,再结合 iostat 判断。
主机资源正常不代表应用正常
应用可能受线程池、连接池、锁、内部队列或超时策略限制。此时主机 CPU 甚至很空闲,但接口仍然会逐渐堆积请求。
Nginx 日志时间不是完整链路监控
request_time 能反映 Nginx 处理请求的时间,但不能单独说明客户端到服务器之间的每一段网络质量,也不能直接给出数据库 SQL 耗时。要确认数据库瓶颈,必须补充应用和数据库侧证据。
容器或资源配额会改变判断方式
如果 Nginx 运行在容器或受 systemd 资源限制的服务中,主机整体还有空闲资源,并不代表该服务没有达到自己的 CPU 或内存上限。应同时核对服务、容器或 cgroup 的配额,再解释 CPU、内存和 OOM 指标。
下一次故障要一起看什么
为了减少单点误判,下一次遇到 Debian Nginx 访问变慢时,至少保留下面这组同时间指标:
- 请求层:
request_time、upstream_connect_time、upstream_header_time、upstream_response_time、状态码和响应大小; - CPU 层:
us、sy、id、wa、运行队列r、高消耗进程; - 内存层:
available、swap 使用与si/so、内存 PSI; - 磁盘层:
await、读写队列、%util、读写速率和进程级 I/O; - 网络层: 收发速率、错误丢包、TCP 重传、监听队列和连接
Send-Q; - 应用与数据库层: 上游错误、应用处理时间、连接池等待、慢查询和锁等待。
当这些指标位于同一个观察窗口内,判断就不再是“哪个数字最高”,而是能够回答“请求在哪个阶段开始排队、哪个资源与延迟同步变化、哪些替代原因已经被排除”。这才是监控系统资源并定位 Nginx 性能瓶颈的有效依据。