上一篇 下一篇 分享链接 返回 返回顶部

海外服务器带宽有余量但下载慢,如何区分链路、磁盘与应用瓶颈?

发布人:Minchunlin 发布时间:2026-10-08 19:27 阅读量:2

下载慢并不等于服务器出口带宽不足。监控中看到网卡出方向只有几百 Mbps,而线路标称为 1 Gbps,只能说明当前没有形成足够大的有效发送流量;单个 TCP 连接可能受跨地域 RTT、丢包、拥塞窗口或对端接收窗口限制,也可能是 Web 服务主动限速、磁盘无法持续供数,甚至应用还在等待数据库生成文件。

排查时应把“建立连接、等待首字节、持续传输”拆开观察,并同时比较受影响地区、不同客户端、服务器本机和公网访问结果。首字节时间明显变长,优先看 Web 入口、应用和数据库;首字节很快但文件主体传输慢,优先看链路、限速、磁盘和连接级资源。只有沿着这条链路逐层排除,才能避免把所有问题都归因于海外线路。

确认症状:先判断慢在响应的哪一段

先统一下载速度的计算口径

下载工具通常显示 MB/s,云监控和带宽规格通常显示 Mbps,两者不能直接比较。按十进制口径计算时:

传输速率 Mbps = 文件大小 GB × 8 × 1000 ÷ 下载耗时秒数

例如,一个 100 MB 的十进制文件在 21.4 秒内完成下载:

  • 文件大小:100 MB = 0.1 GB
  • 文件对应的数据量:100 × 8 = 800 Mb
  • 平均速率:800 ÷ 21.4 ≈ 37.4 Mbps
  • 换算成字节速率:约 4.67 MB/s

如果服务器有 1 Gbps 出口,但实际只有一条连接以 37.4 Mbps 下载,网卡利用率自然可能只有几十分之一。此时“带宽还有余量”与“用户下载很慢”并不矛盾。

再比如 1 GB 文件用了 120 秒:

  • 1 GB = 8000 Mb
  • 平均速率 = 8000 ÷ 120 ≈ 66.7 Mbps

如果监控显示整台服务器出方向约 70 Mbps,这个结果反而与该下载任务基本一致,不能据此认为线路还应自动达到 1 Gbps。

如果使用 GiB、MiB 等二进制单位,应单独记录口径。不要把 1 GiB 直接当成 1000 MB,也不要把 MB/s 直接当成 Mbps。

记录三个关键时间

一次下载至少要区分以下三个阶段:

阶段主要指标典型含义
建立连接DNS、TCP、TLS 时间域名解析、路由、握手或证书协商是否缓慢
等待首字节TTFB、starttransferWeb 入口、应用、数据库或上游存储是否在准备响应
传输响应体总耗时、speed_download、重传链路、限速、磁盘读取、发送缓冲和客户端接收是否受限

可以用 curl 做基准测试。下面的结果是命令格式示例,不代表某个实际环境的测试结果:

curl --location --fail --silent --show-error \
  --output /dev/null \
  --write-out 'http_code=%{http_code}\nremote_ip=%{remote_ip}\nsize=%{size_download} bytes\ntime_namelookup=%{time_namelookup}s\ntime_connect=%{time_connect}s\ntime_appconnect=%{time_appconnect}s\ntime_starttransfer=%{time_starttransfer}s\ntime_total=%{time_total}s\nspeed_download=%{speed_download} bytes/s\n' \
  'https://download.example.com/files/test.bin'

例如,若输出接近以下形式:

http_code=200
remote_ip=203.0.113.10
size=104857600 bytes
time_namelookup=0.012s
time_connect=0.041s
time_appconnect=0.086s
time_starttransfer=0.090s
time_total=21.420s
speed_download=4890000 bytes/s

这表示首字节约 0.09 秒就返回,但主体传输了 21 秒左右。排查重点应放在传输阶段,而不是优先检查数据库查询。

反过来,如果结果是:

time_starttransfer=18.600s
time_total=21.100s

那么大部分时间消耗在首字节之前。即使下载完成后的平均速度看起来不高,也不能直接归因于线路,因为应用已经花了很长时间准备响应。

确认症状:先判断慢在响应的哪一段 / 记录三个关键时间配图

明确影响范围

需要记录以下维度,而不是只记录“有人反馈下载慢”:

  • 是否所有文件都慢,还是只有大文件、某一类文件或动态导出文件慢;
  • 是否所有用户都慢,还是集中在某个国家、地区、运营商或办公网络;
  • 是否所有协议地址都慢,还是 IPv4 与 IPv6 其中一条路径异常;
  • 是首次下载慢,还是缓存命中后的持续下载也慢;
  • 服务器本机访问是否正常;
  • 多个并发下载是否都慢,还是单连接慢而并发后速度明显提升;
  • 服务器网卡总流量是否低,单个连接实际发送速率是多少。

浏览器的下载速度还可能受到浏览器扩展、代理、磁盘写入、HTTP/2 多路复用和断点续传影响。诊断时最好使用固定 URL、固定文件、固定客户端,并用命令行结果与浏览器现象相互核对。

建立原因树:带宽余量不代表没有瓶颈

可以把下载链路拆成五层。每一层都有自己的观察证据,不能用另一层的指标替代。

层级需要回答的问题常见现象下一步
网络链路客户端到服务器的路径是否存在高 RTT、丢包或拥塞某地区慢、跨运营商慢、单连接速率低从受影响地区测试路径、RTT、重传和 IPv4/IPv6
Web 入口Nginx、负载均衡、CDN 或网关是否限速或排队首字节正常但响应体被固定速度限制查响应头、入口配置、请求日志和上游耗时
系统与磁盘文件能否持续从存储读出,系统是否在等待 I/O%iowait 高、磁盘读延迟高、发送速率随磁盘抖动对比磁盘读指标、进程 I/O 和文件类型
应用服务文件是否由程序实时生成、压缩、加密或分块输出TTFB 高、工作线程占满、应用队列变长查看应用耗时、线程池、生成任务和错误日志
数据库与上游应用是否在查询数据或等待对象存储、内部服务查询慢、连接池耗尽、导出边生成边发送区分元数据查询和持续流式生成,检查只读指标

这里还有一个重要边界:静态文件由 Web 服务器直接读取时,数据库通常不在持续下载链路上。即使用户登录、权限验证需要数据库,数据库也可能只影响首字节时间;文件已经开始发送后,主体速度更可能由磁盘、Web 发送策略或网络决定。只有动态导出、实时打包、分块生成等场景,数据库才可能持续影响响应体传输。

建立原因树:带宽余量不代表没有瓶颈配图

第一层排除:确认是否为跨地域链路问题

看公网路径,而不是只看服务器网卡

海外服务器面对的是一条端到端路径,包括用户本地网络、接入运营商、国际出口、跨境或跨洲链路、服务器机房入口以及服务器自身网络栈。服务器端带宽监控只能看到最后一段,无法说明中间路径没有拥塞。

如果只有某个地区或某个运营商的用户慢,而服务器本机、其他地区访问正常,链路问题的优先级就会上升。建议从受影响区域和未受影响区域分别执行相同的下载测试,比较:

  • 到达服务器的 RTT;
  • 中间节点是否出现持续丢包或延迟抖动;
  • 下载期间 TCP 重传是否增加;
  • IPv4 和 IPv6 是否使用了不同路径;
  • 访问的公网 IP 是否一致,是否被解析到不同入口。

可以使用以下命令观察路径。mtr 需要在测试端已经安装,目标地址应替换为实际下载入口的 IP 或域名:

mtr -rwzc 50 203.0.113.10

如果需要观察从服务器返回客户端的方向,应在服务器上对测试客户端公网 IP 执行同类检查,因为互联网路径通常不完全对称。

中间某一跳显示丢包,不一定代表真实故障。许多路由器会限制 ICMP 响应,但仍能正常转发业务流量。判断依据应是:丢包或高延迟是否从该节点开始持续到最终目标,并且是否与 TCP 重传、下载速率下降同时出现。只看某个中间节点的百分比,容易产生误判。

检查 TCP 连接是否受到窗口和重传影响

在下载进行时,可以查看连接状态:

ss -tin dst 203.0.113.10:443

不同 Linux 版本的输出字段可能略有差异。以下是用于说明的模拟片段:

cubic wscale:8,7 rto:420 rtt:186/24.2 ato:40 mss:1448
cwnd:10 bytes_acked:184320 retrans:3/120

重点关注:

  • rtt:往返时延及其波动;
  • cwnd:拥塞窗口是否长期偏小;
  • retrans:是否出现持续重传;
  • rto:重传超时是否升高;
  • mss:是否因为路径 MTU 或隧道封装等因素明显偏小。

高 RTT 本身不一定等于故障,但它会延长 TCP 慢启动和确认反馈周期。若同时存在丢包,发送窗口会收缩,单连接吞吐可能远低于物理带宽。此时服务器总出口利用率仍然很低,因为瓶颈在该连接的有效发送窗口,而不是整条线路已经被占满。

还可以查看网卡层面的错误和丢包:

ip -br link
ip -s link show dev eth0

实际网卡名称应以 ip -br link 的结果为准。重点查看 RX/TX errors、dropped、overruns 等计数是否在测试期间持续增加。计数器历史上有少量累积不一定说明当前异常,应在下载前后取差值。

对比 IPv4、IPv6 和不同入口

如果域名同时配置了 A 和 AAAA 记录,可以分别测试:

curl -4 --location --fail --silent --show-error \
  --output /dev/null \
  --write-out 'ipv4 total=%{time_total}s speed=%{speed_download} bytes/s remote=%{remote_ip}\n' \
  'https://download.example.com/files/test.bin'

curl -6 --location --fail --silent --show-error \
  --output /dev/null \
  --write-out 'ipv6 total=%{time_total}s speed=%{speed_download} bytes/s remote=%{remote_ip}\n' \
  'https://download.example.com/files/test.bin'

如果 IPv4 明显正常而 IPv6 明显缓慢,应继续核对 AAAA 记录、IPv6 路由、机房上游和客户端网络,而不是笼统地说“海外带宽不够”。如果两者都慢,但只在某个地区慢,则更像该地区到机房的路径或入口问题。

用多连接测试验证“单连接瓶颈”

在受控文件和非高峰时段,可以将单连接与少量并发连接进行对比。这个测试不能作为生产压力测试,也不能为了“跑满带宽”随意增加并发。

如果一个连接只有约 20 Mbps,四个连接合计接近 80 Mbps,可能说明:

  • 单连接受到 TCP 窗口、RTT、丢包或服务端单连接限速影响;
  • Web 入口对每个客户端或每个连接设置了速率上限;
  • 客户端或浏览器的单连接策略限制了速度。

但多连接更快并不能直接证明网络有问题。它也可能绕过了应用层每连接限速,或者只是多个请求分别命中了不同的后端。需要结合入口配置和 ss 连接状态判断。

第二层排除:检查 Web 入口、反向代理和限速策略

先看响应头和状态码

下载请求可能经过 DNS、CDN、负载均衡、Nginx、Apache、应用网关或对象存储。每一层都可能改变速度。先查看响应头:

curl --silent --show-error \
  --dump-header - \
  --output /dev/null \
  'https://download.example.com/files/test.bin'

重点检查:

  • HTTP/1.1 200 或 206 Partial Content 是否符合预期;
  • 是否发生了 301、302 等跳转;
  • Content-Length 是否存在且与文件大小合理;
  • 是否返回 Transfer-Encoding: chunked;
  • 是否支持 Accept-Ranges: bytes;
  • Content-Encoding 是否对下载文件进行了不必要的压缩;
  • 是否存在入口或上游生成的限速相关响应头;
  • Age、缓存命中标识、上游节点标识是否在不同地区发生变化。

curl -I 只发出 HEAD 请求,某些应用对 HEAD 和 GET 的处理不同,因此不能用 HEAD 的结果完全代替实际下载。响应头检查应与真正 GET 的 time_starttransfer 和 speed_download 配合使用。

区分固定限速和自然变慢

Web 入口常见的限制包括:

  • 按客户端 IP 限速;
  • 按连接数限速;
  • 按 URL、用户等级或文件类型限速;
  • limit_rate、limit_rate_after 等响应级策略;
  • 应用通过响应头向 Nginx 传递的限速值;
  • 负载均衡后端连接池或发送缓冲不足;
  • CDN 边缘节点到源站的回源速度受限。

如果每次测试都稳定在接近某个固定值,例如约 40 Mbps,且 RTT 和重传没有明显变化,应优先检查配置限速。Nginx 中 limit_rate 5m 这类配置表示大约 5 MB/s 的响应速率,按十进制近似相当于 40 Mbps,实际值还会受单位换算、协议开销和调度影响。

如果速度在 1 MB/s、10 MB/s、30 MB/s 之间剧烈波动,同时磁盘读延迟、TCP 重传或应用 CPU 也同步变化,则更像资源或链路波动,而不是固定限速。

用访问日志拆开入口时间和上游时间

Nginx 日志中常见的两个字段是:

  • $request_time:从读取请求到完成响应发送的总时间;
  • $upstream_response_time:等待上游响应的时间,可能包括应用或内部服务耗时;
  • $body_bytes_sent:发送给客户端的响应体字节数;
  • $status:HTTP 状态码;
  • $request_length:请求大小;
  • $http_range:客户端是否使用了 Range 请求。

下面是一个日志格式示例,仅用于说明应记录哪些字段:

log_format download_timing
    '$remote_addr "$request" '
    'status=$status body_bytes=$body_bytes_sent '
    'request_time=$request_time '
    'upstream_time=$upstream_response_time '
    'range="$http_range" '
    'user_agent="$http_user_agent"';

示例日志:

198.51.100.20 "GET /files/test.bin HTTP/1.1" status=200 body_bytes=104857600 request_time=21.420 upstream_time=0.082 range="-"

这个结果表示上游约 0.082 秒就返回了,Nginx 最终用了 21.420 秒发送完主体,重点应转向网络、Nginx 限速、磁盘发送能力或客户端路径。

另一个示例:

198.51.100.20 "GET /export?id=7812 HTTP/1.1" status=200 body_bytes=104857600 request_time=21.420 upstream_time=18.900 range="-"

此时大部分时间花在上游准备响应上,应用、数据库或内部存储应排在链路之前检查。

如果需要查看当前生效的 Nginx 配置,可以使用只读检查:

nginx -T

生产环境不要为了验证问题直接删除限速、改动代理缓冲或覆盖整个配置。若确实需要调整,应先备份当前配置,限制影响到一个测试路径或少量流量,先执行 nginx -t 校验,再在可控时间窗口 reload;验证失败时恢复备份配置并重新校验。修改前应确认当前 Nginx 版本、配置文件路径和运行用户,不能照搬其他发行版的路径。

第三层排除:判断磁盘和系统是否供不上数据

静态文件也可能被存储拖慢

Web 服务器发送文件前,需要持续从文件系统读取数据。以下情况可能造成网卡带宽有余量,但下载速度仍低:

  • 文件位于繁忙的网络盘、块存储或机械盘;
  • 同一时间有备份、扫描、压缩、日志轮转或大量随机 I/O;
  • 内存不足,文件缓存频繁被回收;
  • 磁盘队列变长,读取请求等待时间上升;
  • 文件系统空间或 inode 接近耗尽;
  • Web 进程文件描述符或发送缓冲不足;
  • CPU 高负载导致用户态或内核态处理不及时。

可以先用只读监控观察系统整体状态:

iostat -xz 1 10
vmstat 1 10
free -h
df -hT
df -ih

iostat 如果未安装,可先使用 vmstat、free 和系统现有监控,不要为了排障在高峰期临时运行高负载磁盘测试。

重点观察:

  • r_await、w_await:读写请求平均等待时间;
  • aqu-sz:设备队列长度;
  • %util:设备忙碌程度;
  • vmstat 中的 si、so:是否发生交换;
  • wa:CPU 等待 I/O 的比例;
  • 文件系统空间和 inode 是否耗尽。

这些指标没有适合所有 SSD、NVMe、云盘的统一阈值。比如 %util 在多队列设备上的解释与单队列设备不同,不能看到 80% 就断定一定满载。更可靠的方法是观察下载速度下降是否与读延迟、队列长度和 I/O 等待同步发生。

区分磁盘慢、缓存命中和应用慢

同一个文件第一次访问慢、随后访问明显变快,可能是首次需要从存储读取,后续命中了页缓存。此时不能只看第二次下载结果。应在相近条件下比较:

  • 文件首次访问与缓存命中的差异;
  • 静态小文件和大文件的差异;
  • 同一磁盘上的其他读取任务;
  • Web 进程的读 I/O 是否随下载增加;
  • 服务器本机读取速度与公网传输速度。

可以查看进程级 I/O:

pidstat -d 1 10

如果 Nginx、Apache 或应用进程的读速率与下载速率同步下降,并且设备 r_await、队列和 wa 同时升高,磁盘或存储层的可能性较大。

四个共享时间轴的小图,展示模拟下载速度下降时读等待、队列和I/O等待上升,再共同恢复;高亮相同时间窗口,不设置通用故障阈值

不要在生产高峰期直接使用大文件 dd、fio 等命令做压力测试。它们可能污染页缓存、制造额外 I/O,甚至影响同一存储卷上的数据库和其他服务。需要验证存储性能时,应使用隔离卷、测试文件和维护窗口,并提前确认测试不会覆盖业务数据。任何涉及覆盖设备或创建测试文件的操作,都应先备份并明确回滚方式。

查看 CPU、内存和文件描述符

如果磁盘等待不高,但发送速度仍低,还要排除系统资源:

top
free -h
vmstat 1 10
ulimit -n
ss -s

应关注:

  • Web 或应用进程是否占满单个 CPU 核;
  • 是否有大量上下文切换;
  • 是否发生 swap;
  • TCP 连接数是否异常增长;
  • 文件描述符是否接近限制;
  • SYN_RECV、TIME_WAIT 或已建立连接是否远超平时。

动态压缩、加密、实时打包、图片处理和导出文件生成,可能让 CPU 成为瓶颈。此时文件并非简单地从磁盘读出,而是每发送一段就要完成计算。若 CPU 使用率升高、应用线程排队,网卡仍不可能跑满。

第四层排除:定位应用和数据库等待

用首字节时间判断应用是否参与了慢响应

下载接口可以分为两类:

  1. 静态直出:文件已经存在,Web 服务器直接读取并发送。
  2. 动态生成:应用查询数据库、读取多个来源、打包、加密或实时生成文件后再发送。

动态接口出现以下现象时,应用层优先级较高:

  • time_starttransfer 长期超过平时;
  • upstream_response_time 接近 request_time;
  • 应用线程池、协程池或任务队列达到上限;
  • 生成文件的 CPU、内存或临时磁盘使用量升高;
  • 相同参数重复请求时,首字节时间随数据库负载变化。

应用日志最好记录请求 ID、用户或任务 ID、鉴权耗时、数据库耗时、文件生成耗时、开始发送时间和完成时间。例如,下面是结构示例:

request_id=8d31 method=GET path=/export
auth_ms=24 db_ms=18420 build_ms=3120
first_byte_ms=21640 send_ms=1920 total_ms=23560
bytes=104857600 status=200

这里数据库和生成阶段用了约 21.5 秒,而实际发送只用了约 1.9 秒。即使服务器出口没有明显流量,也不能把问题归为网络。

相反,如果日志接近以下形式:

request_id=91af method=GET path=/files/test.bin
auth_ms=8 db_ms=0 build_ms=0
first_byte_ms=35 send_ms=21400
bytes=104857600 status=200

应用和数据库基本没有持续参与,应该转向入口、存储和链路。

数据库只在真正参与下载时才是重点

数据库可能影响下载的场景包括:

  • 根据用户权限查询可下载文件;
  • 生成 CSV、报表或备份文件;
  • 根据筛选条件实时导出数据;
  • 边查询边序列化或压缩;
  • 生成一次性下载链接前需要复杂关联查询。

排查时需要分别记录“查询完成时间”和“开始发送时间”。如果数据库查询只是生成签名 URL 前的一次短查询,它通常只影响首字节;如果应用使用游标持续查询并边读边发送,数据库延迟和连接池则可能持续影响响应体。

数据库排查优先使用已有的慢查询日志、连接池指标、查询耗时分布和锁等待指标。需要查看执行计划时,应先使用不会执行查询的 EXPLAIN,不要在生产高峰期直接运行会实际执行并放大负载的分析语句。涉及索引、表结构、参数或数据修改时,应先备份、评估锁影响,并准备回滚方案;不要为了验证下载速度直接清理、重建或修改业务数据。

检查上游对象存储和内部服务

有些下载 URL 由应用转发到对象存储、文件服务或内部 API。此时服务器出口带宽有余量,可能只是服务器到上游的读取速度不足。

应分别测量:

  • 应用收到请求到开始访问上游的时间;
  • 上游连接建立和首字节时间;
  • 上游响应体读取速度;
  • 应用转发给客户端的速度;
  • 上游是否对单连接、签名 URL 或出口 IP 限速。

如果应用从上游读取只有 5 MB/s,再大的服务器公网带宽也无法让客户端获得更高速度。此类问题需要查看上游服务指标和应用转发日志,不宜单独调整本机网卡或 Nginx 缓冲区。

A5数据为海外文件下载与业务接口提供多地区物理服务器资源,覆盖中国香港、美国、日本等地。香港产品提供CN2与国际带宽选择,美国常规系列提供CN2 GIA线路;不同系列的NVMe存储、大内存和多核计算配置,可为静态文件分发、数据库缓存及动态文件生成提供硬件基础。香港存储系列另有大容量企业级硬盘方案,满足文件库、备份与归档需求,形成覆盖网络、计算和存储的资源选择。

逐层排除:用低风险测试缩小范围

下面是一套适合生产排障的顺序,先获取信息,再做范围有限的验证。

1. 固定测试对象和时间窗口

选择一个已知大小的文件或固定参数的导出任务,记录:

  • 文件大小;
  • URL 和最终响应 IP;
  • 客户端地区、运营商和公网 IP;
  • IPv4 或 IPv6;
  • HTTP 状态码;
  • 首字节时间、总耗时和平均速度;
  • 是否经过 CDN、负载均衡或其他网关。

同一时间不要混用浏览器、下载器和命令行结果。不同工具可能使用不同的连接数、Range 请求和缓存策略。

2. 做服务器本机与公网对比

本机测试可以判断 Web 入口、应用和文件读取是否大致正常,但不能代表公网链路。应至少比较以下结果:

本机结果公网结果优先怀疑方向
快慢跨地域链路、入口限速、客户端路径、IPv4/IPv6
慢同样慢磁盘、应用、数据库、上游存储或本机 Web 服务
首字节慢首字节也慢应用、数据库、内部上游
首字节快、主体慢首字节快、主体也慢网络、限速、磁盘发送、客户端接收
本机快且公网只有部分地区慢受影响地区路径或边缘入口
多连接明显快于单连接单连接 TCP、每连接限速或入口策略

本机测试要尽量保持相同的 Host、路径和认证逻辑。直接访问 127.0.0.1 可能绕过公网负载均衡、TLS、CDN 和部分 Nginx 配置,因此只能作为局部隔离测试。

3. 查看入口日志,再决定是否查应用

如果 request_time 很长而 upstream_response_time 很短,先查网络、磁盘、限速和客户端路径。如果两个时间都很长,再进入应用和数据库分支。

如果日志没有记录 $body_bytes_sent、请求耗时或 Range 信息,可以先补充低成本日志字段。修改日志格式不会自动修复故障,但能减少对单一带宽图的猜测。修改配置前应备份文件并确认日志量、磁盘空间和隐私字段,避免为了排障造成日志盘写满。

4. 在下载期间同时采集系统和 TCP 指标

测试不要只执行一次命令,而应让下载和监控同时进行。至少保留:

  • iostat 或云监控中的磁盘读延迟;
  • vmstat 中的 I/O 等待;
  • CPU、内存和 swap;
  • ss -tin 中的 RTT、拥塞窗口和重传;
  • ip -s link 中的网卡错误和丢包;
  • 进程级读取量和 CPU 使用率。

如果这些指标都正常,但某个地区速度低,链路和入口路径的优先级上升。如果磁盘等待明显升高,先处理存储争用;如果应用线程或数据库连接池耗尽,则不要继续做网络调优。

5. 用小范围变更验证假设

排障验证应一次只改变一个变量。例如:

  • 对同一个文件临时绕过动态生成逻辑;
  • 只对测试 URL 取消应用层压缩;
  • 在单个后端节点上观察静态文件直出;
  • 将一个测试文件放到另一块已知空闲的存储;
  • 在维护窗口对一个测试入口调整限速;
  • 对同一客户端分别固定 IPv4 和 IPv6。

任何配置变更都要记录原值、影响范围、开始时间和回滚命令。验证成功不代表可以直接推广到全部下载接口,还要检查 CPU、磁盘、连接数、错误率和费用风险。

定位根因:几个容易混淆的典型场景

场景一:出口带宽低,但某地区单连接很慢

表现可能是:

  • 服务器本机下载正常;
  • 其他地区用户正常;
  • 受影响地区 RTT 较高;
  • ss 显示重传增加,拥塞窗口反复收缩;
  • 多连接总速度明显高于单连接。

这时应优先保留受影响区域的路径、时间和 TCP 证据,检查机房上游或相关入口。不要因为服务器网卡只有 100 Mbps 就直接升级到更高带宽;如果瓶颈在跨地域路径,升级带宽未必改变单连接体验。

场景二:固定速度接近某个数值

表现可能是:

  • 不同时间测试速度都接近同一档;
  • 首字节时间正常;
  • RTT 和重传没有明显变化;
  • Nginx、应用网关、CDN 或账号策略存在每连接限速;
  • 多个文件都被限制在相近速度。

此时应沿入口链路搜索 limit_rate、用户配额、节点策略、服务商限速和应用返回的限速标记。不要仅根据网卡利用率判断线路故障。

场景三:文件下载期间磁盘队列明显升高

表现可能是:

  • 服务器本机下载也慢;
  • iostat 的读等待和队列长度上升;
  • Web 进程读 I/O 与下载速度同步波动;
  • 备份、扫描、压缩或其他大文件任务同时运行;
  • 关闭或错峰后速度恢复。

此时根因可能是存储争用、文件所在卷性能不足或文件系统缓存压力。处理方向包括错开高 I/O 任务、改善文件存储层、使用适合的缓存策略或把静态文件与数据库放到不同资源池,而不是先调整 TCP 参数。

场景四:首字节等待十几秒,主体传输很快

表现可能是:

  • time_starttransfer 高;
  • upstream_response_time 接近总耗时;
  • 应用日志显示数据库查询、报表生成或打包耗时;
  • 一旦开始发送,网卡速率和磁盘读取都正常。

此时数据库或应用才是主要瓶颈。应拆分权限查询、数据查询、文件生成和发送阶段,避免把已经生成的文件每次都实时重算。若改为异步生成或缓存结果,还需要评估数据时效、权限隔离和临时文件清理策略。

场景五:服务器本机和应用都正常,但浏览器仍显示慢

还要检查客户端侧:

  • 浏览器是否使用了不同的 DNS 结果;
  • 是否发生重定向;
  • 是否请求了 Range 分片;
  • 浏览器所在网络是否存在上传或下载竞争;
  • 本地磁盘是否成为写入瓶颈;
  • 是否只有某个浏览器或下载器出现问题。

命令行从相同网络出口访问,可以帮助排除浏览器展示层问题。但如果命令行与浏览器使用的连接方式不同,也要对照请求头和最终响应 IP,不能简单把两者结果混为一谈。

验证恢复:不要只看带宽图恢复正常

根因处理后,需要使用相同文件、相同地区、相同协议地址和相近时间窗口重新测试。恢复验证至少包括:

  1. 首字节时间回到正常基线;
  2. 文件主体速度提升,而不是只出现短暂突发;
  3. HTTP 状态码、文件大小和校验值正确;
  4. TCP 重传、网卡错误和磁盘等待没有同步恶化;
  5. 应用线程、数据库连接池和上游服务没有出现新的排队;
  6. 多个用户同时下载时,错误率和其他业务接口仍保持正常。

例如,原来 100 MB 文件首字节为 18 秒、总耗时 24 秒;调整数据库查询后首字节降到 0.3 秒,但总耗时仍为 23 秒,说明只修复了准备阶段,传输阶段的链路、限速或存储问题还没有解决。反过来,如果首字节一直正常,取消限速后速度提高,但磁盘 %util 和其他业务延迟明显上升,也不能把变更直接视为完整修复。

涉及 Nginx、网关或应用配置的变更,应保留变更前版本。验证失败时恢复原配置,执行配置语法检查,再在可控窗口 reload;不要通过删除配置文件、覆盖整个目录或重启所有服务来“碰运气”。涉及数据库参数、索引、权限和防火墙的变更,还要单独准备备份、影响评估和回滚方案。

复发时应保留的监控点

要避免下次再次陷入“带宽明明没跑满”的争论,建议按下载链路持续记录以下指标:

  • 接口层:请求量、HTTP 状态码、首字节时间、总耗时、响应体字节数、平均下载速度、Range 请求比例;
  • 入口层:request_time、upstream_response_time、每个后端节点的响应时间、限速命中次数、连接数和队列长度;
  • 网络层:出方向 Mbps、pps、网卡错误与丢包、TCP 重传、RTT,按地区、运营商、IPv4/IPv6 和入口 IP 分组;
  • 系统层:CPU 用户态与内核态占用、I/O 等待、内存和 swap、文件描述符、TCP 连接状态;
  • 存储层:读吞吐、读 IOPS、读延迟、队列长度、卷的错误和限流事件;
  • 应用层:鉴权耗时、数据库耗时、文件生成耗时、线程池、任务队列、临时文件空间;
  • 数据库层:查询延迟分位数、慢查询数量、锁等待、连接池使用率和失败率;
  • 用户体验层:按地区记录固定文件的下载成功率、首字节时间和完整下载耗时。

告警不要只设置“出口带宽超过某个百分比”。更有价值的组合条件是:某地区下载速度持续低于基线,同时首字节正常、TCP 重传升高;或者首字节时间持续升高,同时应用队列和数据库延迟上升;又或者磁盘读等待升高并与多个下载接口同时变慢。这样才能把链路、磁盘和应用瓶颈区分开,并在带宽尚未跑满时提前发现真正影响用户下载的环节。