9.2 KiB
tags, create time
| tags | create time | ||||||||
|---|---|---|---|---|---|---|---|---|---|
|
2026-05-17 22:40 |
带宽、延迟、RTT、吞吐量
概述
理解网络性能的核心指标,是优化应用的前提。带宽决定"管道有多粗",延迟决定"跑一程要多久",RTT 是往返一次的时间,而吞吐量是实际能搬多少数据。它们之间的关系经常影响系统架构设计。
核心定义
带宽(Bandwidth)
单位时间内能传输的最大比特数,通常用 bps(bits per second)表示:
1 Gbps = 10⁹ bits/s = 125 MB/s(注意:bit vs Byte)
| 链路类型 | 理论带宽 | 实际可用 |
|---|---|---|
| 千兆以太网 (GbE) | 1 Gbps | ~940 Mbps(协议开销) |
| Wi-Fi 6 (802.11ax) | 9.6 Gbps | ~1.2–2 Gbps(理论峰值) |
| SATA III | 6 Gbps | 600 MB/s 实际 |
| PCIe 4.0 x16 | 32 Gbps | ~3.9 GB/s |
[!tip] bit vs Byte:记住除 8 运营商说的 "100M 宽带" 是 100 Mbps。换算成下载速度约
100 ÷ 8 ≈ 12.5 MB/s。
延迟(Latency)
一个数据包从发送到被接收所经历的总时间,由四部分组成:
\text{Latency} = \text{传播延迟} + \text{传输延迟} + \text{排队延迟} + \text{处理延迟}
flowchart LR
A["传播延迟<br/>Propagation"] --> B["传输延迟<br/>Transmission"]
B --> C["排队延迟<br/>Queuing"]
C --> D["处理延迟<br/>Processing"]
style A fill:#DDA0DD,color:#000
style B fill:#87CEEB,color:#000
style C fill:#FFD700,color:#000
style D fill:#98FB98,color:#000
| 延迟类型 | 公式 | 取决于 | 典型值 |
|---|---|---|---|
| 传播延迟 | \frac{\text{Distance}}{v} |
物理距离、介质 | 光纤中 v \approx 2 \times 10^8 m/s(真空中光速的 2/3) |
| 传输延迟 | \frac{\text{Packet Size}}{\text{Bandwidth}} |
数据包大小、链路带宽 | 1500B 以太网帧在 1Gbps 链路上 ≈ 12 μs |
| 排队延迟 | 不确定 | 路由器拥塞程度 | 空闲时 ≈ 0,拥塞时可达数百 ms |
| 处理延迟 | 不确定 | 路由器/交换机性能 | 通常 < 1 ms |
[!question] 这四类延迟,哪个最"不可控"? 排队延迟。传播延迟由物理距离决定(选不了机房位置就改不了),传输延迟由包大小和带宽决定,处理延迟通常很小。但排队延迟随网络拥塞程度剧烈波动——这也是为什么它直接关联抖动(Jitter) 和 丢包。
| 场景 | 典型单向延迟(主要贡献:传播延迟) |
|---|---|
| 同机房内 | 0.1–1 ms |
| 同城(同一城市数据中心) | 1–5 ms |
| 跨省(国内) | 20–40 ms |
| 跨洋(中美) | 80–150 ms |
| 卫星轨道(LEO) | 20–40 ms |
[!tip] TCP + TLS 的额外延迟 建立一个 HTTPS 连接还需要 TCP 三次握手(1 RTT)+ TLS 1.3 握手(1 RTT),在数据发出前就已经"花掉"了 2 个 RTT。这也是 HTTP/2、连接复用和 TLS Session Resumption 存在的重要原因。
RTT(Round-Trip Time)
从发送方发出数据包到收到确认应答的总时间。严格来说:
\text{RTT} = 2 \times (\text{传播延迟} + \text{传输延迟} + \text{排队延迟} + \text{处理延迟})
实际测量中,ping 工具测得的 RTT 主要反映传播延迟(往返),而 TCP 的 RTT 估算(用于计算 RTO)还需包含协议栈处理时间。
flowchart LR
A["t=0ms: 发送 SYN"] -->|"传播+路由器处理"| B["t=5ms: 到达服务端"]
B -->|"处理+准备响应"| C["t=5.1ms: 服务端回发 SYN-ACK"]
C -->|"传播+路由器处理"| D["t=10.2ms: 客户端收到 → RTT = 10.2ms"]
style A fill:#DDA0DD,color:#000
style D fill:#98FB98,color:#000
抖动(Jitter)
连续数据包之间延迟的变化量。即使平均延迟很低,抖动大也会严重影响体验:
\text{Jitter} = |\text{RTT}_n - \text{RTT}_{n-1}|
| 应用类型 | 对抖动的敏感度 | 说明 |
|---|---|---|
| 语音通话 / 视频会议 | 🔴 极高 | 抖动 > 30ms 就会出现卡顿、断续 |
| 在线游戏 | 🔴 高 | 操作响应不稳定,玩家体验崩塌 |
| 文件下载 | 🟢 无所谓 | 只关心总吞吐量,不在乎每个包的到达节奏 |
| HTTP API 调用 | 🟡 中等 | 长尾延迟影响 P99 响应时间 |
[!question] 如何对抗抖动? 答案是接收端缓冲(Jitter Buffer)。音视频应用会在接收端先攒一小段数据再播放,用少量额外延迟换取平滑输出。WebRTC 的 jitter buffer 通常 20–200ms。
吞吐量(Throughput)
单位时间内实际成功传输的数据量,受限于最窄环节:
\text{Throughput} = \min(\text{带宽}, \text{拥塞窗口限制}, \text{应用处理能力})
实际吞吐量永远 ≤ 带宽。常见瓶颈:
flowchart LR
A["磁盘IO<br/>可能 < 网卡"] --> B["TCP收发缓冲区"]
B --> C["网络带宽"]
C --> D["对端带宽"]
D --> E["对端CPU/应用"]
F["中间路由拥塞"] -.-> C
G["丢包重传"] -.-> C
style C fill:#FFD700,color:#000
style F fill:#FF6B6B,color:#fff
style G fill:#FF6B6B,color:#fff
[!question] 为什么我的千兆网卡只跑到 100MB/s? 因为千兆以太网理论上限是
1Gbps ÷ 8 = 125MB/s,去掉以太网帧头、IP 头、TCP 头等协议开销后,纯 Payload 大约 94 MB/s。如果还用了 HTTPS/TLS,CPU 加解密还会进一步限制吞吐量。
[!info] Goodput vs Throughput:别搞混 Throughput(吞吐量)是单位时间内传输的总数据量(含协议头)。Goodput(有效吞吐量)是应用层实际收到的有效数据量,要扣除所有协议头、重传包、确认包等开销。
\text{Goodput} < \text{Throughput} \leq \text{Bandwidth}用
iperf3测出来的通常是 throughput;你的文件下载速度反映的更接近 goodput。
带宽时延积(BDP)
BDP(Bandwidth-Delay Product) 定义了链路上"在途"数据的最大量——这是维持满带宽所需的发送缓冲大小:
\text{BDP} = \text{Bandwidth} \times \text{RTT}
实际意义
假设带宽 1 Gbps,RTT = 100 ms:
\text{BDP} = 10^9 \text{ bps} \times 0.1s = 10^8 \text{ bits} = 12.5 \text{ MB}
这意味着:
- TCP 发送窗口至少要有 12.5 MB,才能填满这条管道
- 如果窗口太小,TCP 会频繁停下来等 ACK,吞吐量上不去
- 这就是 Linux 的
net.core.rmem_max/wmem_max要调大的原因
flowchart TD
Pipe["管道: 1Gbps, RTT=100ms<br/>BDP = 12.5MB"]
Small["发送窗口 1MB ❌<br/>只能填 8% 管道 → 吞吐量 ≈ 80Mbps"]
Big["发送窗口 25MB ✅<br/>填满管道 → 吞吐量 ≈ 1Gbps"]
Pipe --> Small
Pipe --> Big
style Small fill:#FF6B6B,color:#fff
style Big fill:#98FB98,color:#000
各场景 BDP 速查
| 链路 | 带宽 | RTT | BDP |
|---|---|---|---|
| 局域网 | 1 Gbps | 0.5 ms | 62.5 KB |
| 同机房 | 10 Gbps | 1 ms | 1.25 MB |
| 同省 | 1 Gbps | 20 ms | 2.5 MB |
| 跨洋 | 100 Mbps | 150 ms | 1.875 MB |
延迟 vs 带宽的典型对比
| 场景 | 带宽瓶颈还是延迟瓶颈? | 说明 |
|---|---|---|
| 大文件传输 | 带宽 | 传输时间长,RTT 占比小 |
| 小请求/高频交互(RPC) | 延迟 | 每个请求 2~3 RTT,带宽几乎不占 |
| 视频流 | 带宽 | 持续大量数据传输 |
| DNS 查询 | 延迟 | 单次查询仅几十字节 |
| Web 首屏加载 | 两者兼有 | TCP+TLS握手(延迟)+ HTML/CSS/JS 下载(带宽) |
Go 中的实践示例
测量 RTT(TCP 连接延迟)
// 测量一次 TCP 连接的 RTT —— 最直接的延迟指标
func measureRTT(addr string) (time.Duration, error) {
start := time.Now()
conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
if err != nil {
return 0, err
}
conn.Close()
return time.Since(start), nil // TCP 握手完成 ≈ 1 RTT
}
按 BDP 调大 TCP 缓冲区
// 高带宽长延迟链路(如跨洋 100Mbps, RTT 150ms)的 BDP = 1.875MB
// 系统默认的 TCP 窗口(128KB~256KB)远远不够
transport := &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
d := net.Dialer{}
conn, err := d.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
tcpConn := conn.(*net.TCPConn)
// 根据 BDP 设置读写缓冲区(2MB,略大于 BDP)
tcpConn.SetReadBuffer(2 << 20)
tcpConn.SetWriteBuffer(2 << 20)
return tcpConn, nil
},
}
[!tip] 为什么不直接用系统默认窗口? Linux 默认
tcp_rmem的最大值通常是 6MB,但单连接的初始窗口只有 ~128KB。对于高 BDP 链路,窗口增长(慢启动)到填满管道需要好几个 RTT,前几秒吞吐量会远低于带宽上限。应用层手动设大缓冲区 + 启用窗口缩放(tcp_window_scaling)可以加速这个过程。
关联笔记
- hhs/NETWORK/01-基础概念/01-OSI与TCP-IP模型对比 — 网络分层中各层的角色
- hhs/NETWORK/09-性能优化/01-TCP内核调优与连接复用 — BDP 对应内核参数 rmem/wmem