单线程大文件传输仅几Mbps,如何区分物理机网络、磁盘与TCP瓶颈?
大文件传输长时间停在几 Mbps,会直接拉长备份、数据迁移和文件交付的窗口。如果同一台物理机在另一项测试中接近 1Gbps,首先要确认两项测试是否走同一条路径、使用相同方向和连接数。网卡协商到 1Gbps、内网测速接近 1Gbps、多连接测速接近 1Gbps,分别证明不同的能力,不能直接证明某条单连接文件传输也能达到相同速度。
区分网络、磁盘与 TCP 瓶颈,关键是逐步缩小数据经过的环节:先用不读写业务文件的 TCP 测试观察传输路径,再比较单连接和多连接,随后加入源文件读取、目标文件写入及原有应用。每一步都同步记录两端资源状态,而不是看到“磁盘忙”“CPU 不高”或“有重传”就直接归因。
一、确认症状:到底是哪一段、哪一种传输慢
统一速率单位和测试口径
网卡和带宽通常用 Mbps、Gbps,文件工具则可能显示 MB/s、MiB/s。本文采用十进制口径:
- 1Gbps = 1000Mbps = 125MB/s,后者是理论换算,不等于实际文件吞吐。
- 5Mbps = 0.625MB/s;5MB/s = 40Mbps,两者相差 8 倍。
- MiB/s 使用二进制字节单位,1MiB = 1,048,576 字节。
例如,传输一个十进制 10GB 文件,平均速率为 6Mbps,理论耗时为:
10 × 8 × 1000 ÷ 6 ≈ 13333 秒,即约 3 小时 42 分钟。
若平均有效吞吐达到 1Gbps,理论耗时为 80 秒。真实耗时还受协议开销、启动阶段及存储读写影响。
所谓“能跑满 1Gbps”,还应注明测量位置。网卡计数器统计的流量、TCP 工具统计的数据量和应用统计的文件字节数,不一定采用相同口径。在标准 MTU、链路较干净且端点处理能力充足的环境中,千兆网络的 TCP 有效吞吐常见于约 900~950Mbps,但不能把这个范围作为所有线路的验收标准。
固定一个可复现的观察窗口
至少记录以下信息:
| 核对项 | 需要确认的内容 | 对判断的影响 |
|---|---|---|
| 传输端点 | 实际发送端、接收端及中间服务 | 避免把内网能力当作跨网能力 |
| 流量方向 | 上传慢、下载慢,还是双向都慢 | 出口和入口、两端处理能力可能不同 |
| 连接数量 | 一个 TCP 连接,还是多个连接 | 多连接总速率不代表单连接速率 |
| 传输阶段 | 刚启动、稳定阶段、接近结束 | 慢启动、缓存和落盘会改变曲线 |
| 文件条件 | 大小、位置、冷热缓存、是否动态生成 | 决定是否包含磁盘或数据库访问 |
| 同时负载 | 备份、其他下载、磁盘任务 | 排除共享资源争用 |
“单线程”也要进一步核实:应用只有一个工作线程,不代表只建立一条 TCP 连接;反过来,一个 TCP 连接也可能由多个处理线程参与。网络诊断应先按单 TCP 流比较,应用诊断再按线程和任务结构比较。
后文命令以 Linux 为例,涉及 iproute2、ethtool、sysstat、iperf3、curl 和 fio,使用前应确认工具已安装。示例中的地址、网卡名和文件路径需要替换为实际值。压测会占用网络或存储资源,应在获准的端点和可控时段执行。
二、建立原因树:把“传文件”拆成一条数据链
一次文件传输通常经过:
源文件或数据源 → 发送端应用 → TCP 发送端 → 网络路径 → TCP 接收端 → 接收端应用 → 目标存储。
这条链的稳定吞吐受其中较慢的环节限制,但各环节还会通过缓冲区相互影响。接收端写盘慢,可能最终表现为 TCP 接收窗口收缩;发送端读取慢,则可能表现为网络一直没有被充分利用。

可以沿下面这棵原因树排查:
| 分支 | 主要观察依据 | 下一步 |
|---|---|---|
| 物理链路与网络路径 | 协商速率、错误增量、路径差异、方向差异 | 用同端点 TCP 测试确认路径能力 |
| TCP 单流 | 单流与多流差距、RTT、拥塞窗口、重传 | 判断时延、丢包、窗口或单流策略 |
| 磁盘与文件系统 | 文件读取和写入速率、延迟、队列 | 隔离源端读取与目标端落盘 |
| CPU 与内存 | 单核利用率、线程状态、换页、软中断 | 查加密、压缩、复制和缓冲压力 |
| 服务与应用 | TCP 测试正常,但原应用慢 | 查限速、协议请求流水线及处理逻辑 |
| 数据库或动态数据源 | 文件需要实时查询、拼接、导出 | 分别测生成阶段和传输阶段 |
可引用的判断规则:多连接能接近带宽上限,只说明路径在该测试条件下具备相应的聚合吞吐;它不能排除单流 TCP、单核处理、应用限速或文件读写瓶颈。
三、先排网络与 TCP:不让业务文件干扰判断
检查本机链路,但不要止步于“1000Mb/s”
先确认业务流量实际走哪块网卡:
ip route get 192.0.2.20
ip -s link show dev eno1
ethtool eno1
ethtool -S eno1
重点查看协商速率、双工状态,以及测试前后错误和丢弃计数是否增长。ethtool -S 的统计项名称由驱动决定,应结合网卡说明判断。
这里有两个边界:
Speed: 1000Mb/s只说明本机接口的协商状态,不代表交换机上联、跨网路径和远端出口也有同等余量。- 非零错误计数可能来自历史事件;只有在慢传输期间持续增长,才更值得沿网卡、线缆、交换机端口继续追查。
丢弃也不全是物理故障:接收队列不足、系统处理不及时同样可能造成丢包。需要结合 CPU、软中断和队列状态判断。
用同一对端点比较单流、多流和反向
在接收端启动测试服务:
iperf3 -s
在发送端依次执行:
iperf3 -c 192.0.2.20 -P 1 -t 30
iperf3 -c 192.0.2.20 -P 4 -t 30
iperf3 -c 192.0.2.20 -P 1 -t 30 -R
默认方向是客户端向服务器发送,-R 则反向发送。-P 1 表示一个并行测试流,不应将其直接理解为操作系统只有一个线程。
测试服务只应向获准的测试端开放,结束后用 Ctrl+C 停止。不要为了测试而直接放开全部防火墙规则。测试也应尽量复用业务的两端和路由,但不同端口仍可能受到不同策略影响,因此结果不能无条件代替业务流量。
| 结果组合 | 初步判断 | 继续验证 |
|---|---|---|
| 单流和多流都慢 | 路径容量、共享拥塞或端点处理能力受限 | 看双向差异、错误增量和资源负载 |
| 单流慢,多流明显快 | 单流 TCP 或单流处理限制 | 看 RTT、窗口、重传及单核状态 |
| TCP 单流快,文件传输慢 | 路径并非首要怀疑对象 | 转向磁盘、服务和应用 |
| 正向慢,反向明显快 | 存在方向性问题 | 对照两端 CPU、接收窗口和出口状态 |
这些结果是分支入口,不是根因结论。尤其是“多流快”,既可能来自多条连接获得更大的合计窗口,也可能来自连接分散到不同处理资源。
用 RTT 和在途数据解释单流上限
TCP 需要在等待确认时保持足够的数据在途。达到目标速率所需的在途数据量,可按带宽时延积估算:
所需在途字节数 = 目标速率(bit/s)× RTT(秒)÷ 8。
例如,RTT 为 80ms,要达到 1Gbps:
1,000,000,000 × 0.08 ÷ 8 = 10,000,000 字节,即约 10MB。
如果一条连接实际只能维持约 60KB 在途数据,那么仅按窗口与 RTT 的关系估算:
60,000 × 8 ÷ 0.08 = 6,000,000bit/s,即约 6Mbps。
这解释了为什么物理带宽很大,单流仍可能很慢。但 60KB 可能来自接收窗口限制,也可能来自拥塞控制、应用供数不足等原因,不能据此直接修改内核缓冲参数。

传输期间可查看连接状态:
ss -tin dst 192.0.2.20
重点观察 rtt、cwnd、mss、重传及窗口相关信息。具体字段随内核和工具版本变化:
cwnd通常以段为单位,可结合 MSS 粗略估算拥塞窗口的字节量。- 拥塞窗口和对端通告的接收窗口共同约束发送。
rcv_space不应直接当作对端当前通告的窗口;部分版本显示的snd_wnd可提供更直接的参考。delivery_rate是连接的速率估计,不等于独立测得的线路容量。
应在慢速阶段连续观察,而不是只截取一张状态图。应用没有持续供数时,较小的窗口或较低的速率估计可能是结果,而非原因。
区分丢包、接收背压和路径限制
若重传持续增长、拥塞窗口反复回落,应进一步检查线路丢包、队列拥塞、接口错误或路径 MTU 问题。普通小包 ping 正常,并不能证明大块 TCP 数据传输正常。
反之,如果重传很少,发送端频繁等待接收窗口,而接收端同时出现写盘延迟或应用读取不及时,应优先怀疑接收背压。
还需注意:
- 中间路由设备对探测报文的限速,可能造成某一跳“丢包高”,但业务流量并未丢失。
- 大数据传输停滞而小请求正常,可能涉及路径 MTU,但需要相应探测或抓包证据。
- RTT 随负载明显上升,通常值得检查排队与共享拥塞,但不能只凭 RTT 指定故障设备。
- 不同端口表现不同,应核对服务和网络策略,而不是立即认定运营商限速。
不要把增大缓冲区、切换拥塞控制或降低 MTU 当成统一答案。先证明问题在哪个分支,再做可回退的单变量调整。
四、再排磁盘与系统:分别观察读取、处理和落盘
同时采集发送端与接收端状态
传输期间,在两端分别观察:
iostat -xz 1 10
mpstat -P ALL 1 10
vmstat 1 10
部分首行是启动以来的平均值,不代表当前一秒,判断时应看后续采样。
磁盘方面,关注吞吐、await、队列和 %util 的联动:
- 磁盘吞吐接近文件速率,延迟和队列持续增长,说明磁盘分支值得深入。
%util高并不总意味着设备吞吐耗尽,尤其不能机械套用于多队列 NVMe。- 网络几 Mbps、块设备几乎没有读写,不能立即认定磁盘没问题:文件可能来自页缓存,也可能位于网络文件系统,需要结合存储类型和应用状态。
CPU 则应按核心观察。一台多核物理机的总 CPU 利用率只有几个百分点,仍可能有一个核心被加密、压缩、校验或协议处理占满。mpstat 能帮助区分用户态处理与软中断压力;进一步可查看对应进程的线程状态。
内存不要只看“空闲多少”。文件缓存占用内存通常是正常现象;持续换页、内存回收压力、脏页积压与吞吐下降同时出现,才更有诊断意义。刚开始很快、随后明显降速,也可能是缓存吸收能力耗尽后暴露了持续写入能力。
先测源文件读取,再隔离目标落盘
若源文件位于支持直接 I/O 的本地文件系统,可在获准时段做只读测试:
fio --name=source-read \
--filename=/srv/data/large-file.bin \
--readonly \
--rw=read \
--bs=1M \
--ioengine=psync \
--iodepth=1 \
--direct=1 \
--time_based \
--runtime=30
该命令只读取指定的现有文件,不应指向原始磁盘或不明路径。即使只读,持续读取也可能影响线上 I/O。
它观察的是单任务顺序读取能力,不完全等同于业务行为。直接 I/O 还可能因文件系统或对齐要求失败;失败只说明测试方式不适用,不能作为磁盘慢的证据。若读取能力为几十或几百 MB/s,而网络传输仅约 0.6MB/s,源端顺序读取通常不是首要瓶颈,但仍需排除业务实际走随机读取、共享存储或其他路径。
对于 HTTP 文件下载,可以把响应写入 /dev/null,暂时去掉接收端文件落盘环节:
curl --fail --silent --show-error \
--output /dev/null \
--write-out 'bytes=%{size_download} speed_Bps=%{speed_download} total_s=%{time_total}\n' \
https://files.example.com/large-file.bin
应替换为获准访问的实际地址,并核对 HTTP 状态和下载字节数,确认收到的是完整文件,而非错误页。speed_download 的单位是字节/秒;乘以 8,再除以 1,000,000,才是 Mbps。该测试仍消耗完整下载流量,也保留了服务端读取、TLS 和客户端接收处理。
可引用的判断规则:同一文件、同一协议、相近负载下,丢弃接收数据明显快于保存文件,才构成目标存储或落盘逻辑受限的线索;如果两者同样慢,应继续检查前面的数据链。

如果原应用要求每块数据同步落盘,或还包含校验、重命名、远端存储确认,那么普通顺序写入测试不能代表它的端到端速度。
五、定位根因:TCP 快而应用慢,应查什么
排查服务限速和单连接工作量
当单流 iperf3 表现正常、原应用仍只有几 Mbps,重点应转向服务与协议:
- 服务是否设置按连接、账号、会话或任务的速率限制?
- 是否在传输过程中做压缩、解压、加密或校验?
- 是否每发送一块数据,就等待应用层确认后才发送下一块?
- 是否读取远端文件、实时生成内容,或频繁执行同步落盘?
- 是否经过反向代理、负载均衡或 CDN,导致两次测试实际走不同路径?
例如,Nginx 的 limit_rate 按响应限速,配置值以字节/秒计。若为 750k,按其单位换算约为 6.144Mbps,可以解释某些接近 6Mbps 的稳定平台。仍需核对生效配置、请求位置及其他限制,不能只凭速度接近便认定命中该设置。
应用层流水线也可能限制吞吐。一个简化示例是:每轮只发送十进制 64KB,等待一次 80ms 确认后再继续,其理论速率约为:
64,000 × 8 ÷ 0.08 = 6.4Mbps。
此时链路、磁盘和 CPU 都可能有余量,真正受限的是应用每轮允许在途的数据量。多线程上传变快,不一定是在修复网络,也可能只是增加了并行请求。
数据库只在实际数据链经过它时排查
传输现成静态文件时,不应因为服务器装有数据库,就把查询性能加入主要原因树。
如果“下载文件”实际上是实时查询、导出和流式发送,则应分别记录查询开始、首字节发出、最后一字节发出及客户端保存完成的时间。首字节等待长,偏向生成阶段;传输中周期性停顿,可能与分批查询、锁等待或应用缓冲有关。
把导出结果预先保存为静态文件后,再走相同下载路径比较,可以帮助区分生成开销与传输开销。相关测试应使用授权数据,不要为排障执行可能影响线上业务的大查询或数据库修改。
用多条证据闭合根因
下面是用于演示判断链的示例组合,并非实际环境测量结果:
| 示例现象 | 更有支持的判断 | 还需补充的验证 |
|---|---|---|
| 单流约 6Mbps,多流明显更快;RTT 较高,单流在途数据不足 | 单流窗口、拥塞或单流处理限制 | 观察窗口来源、重传和核心负载 |
| 单流 TCP 接近链路能力;应用慢且一个核心接近满载 | 应用单核处理受限 | 对照压缩、加密、校验等实际工作 |
| 丢弃数据快,保存文件慢;接收端延迟和队列同步上升 | 目标存储或写入逻辑受限 | 对照持续写入与同步落盘要求 |
| 读取快、TCP 快,应用稳定停在固定速率 | 服务限速或应用流水线限制 | 核对生效配置和请求行为 |
| 正反向 TCP 都慢,接口错误在测试中持续增加 | 本地链路分支可疑 | 对照交换机端口、线缆和网卡统计 |
可靠的根因判断应同时具备:对应资源或机制的异常、同时间窗口内的关联,以及移除该因素后的改善。只满足其中一项,通常只能称为线索。
六、验证恢复,并留下复发监控点
恢复验证要回到最初的业务路径,而不是只看某项压测变快。保持端点、方向、文件、协议、连接数和负载条件尽量一致,每次只改变一个已经获得证据支持的因素。涉及配置修改时,应先保留原配置、明确影响范围,并准备恢复方法。
验证时建议连续检查:
- 单 TCP 流是否改善,多流与单流之间的差距是否符合路径条件。
- 原有大文件传输是否在稳定阶段持续改善,而非只在开始几秒变快。
- 文件是否完整,校验、落盘和应用完成状态是否正常。
- 重传、RTT、磁盘延迟和 CPU 是否出现新的异常。
- 同机其他业务是否受到测试或调整影响。
文件要足够大,测试持续时间要足够长,才能暴露缓存耗尽、持续写入、线路波动和应用周期性等待。总平均速率可用于衡量任务耗时,但定位故障还需要速率随时间变化的曲线。
为避免再次出现“只能看到下载慢、无法知道哪里慢”,可保留以下监控:
| 层级 | 建议监控点 | 主要用途 |
|---|---|---|
| 网络接口 | 收发速率、错误和丢弃增量 | 发现链路及接收处理异常 |
| TCP | RTT、重传、窗口和单流吞吐 | 区分路径拥塞与端点背压 |
| 存储 | 吞吐、延迟、队列、持续写入状态 | 发现读写瓶颈与缓存后降速 |
| 系统 | 各核心利用率、软中断、换页、脏页 | 避免总利用率掩盖局部瓶颈 |
| 应用 | 首字节时间、分段速率、任务耗时、限速状态 | 分离生成、传输与落盘阶段 |
物理机“能跑到 1Gbps”和“单连接传文件只有几 Mbps”可以同时成立。应把前者写清楚测试条件,把后者拆成可重复验证的数据链:同路径 TCP 是否慢,单流为何慢,文件读取是否供得上,接收端是否写得下,应用是否持续发送。这样才能把一次性能现象变成有依据、可验证、可持续监控的根因判断。



