本文摘要:并发拉到16批量下载反而更慢,根因是服务端按IP限速而非带宽不足。从格式选择器、线程叠加与分离流合并三条排查线定位触发条件。适合一次性批量归档,不适合追求极速或高频拉取场景。
一、问题与结论
结论 :-P 16 + --concurrent-fragments 4 产生 64 个瞬时连接,触发服务端按 IP 限速,总耗时反而超过 -P 3 + --concurrent-fragments 1 的保守方案。
场景:对 1000 个视频做离线归档。直觉判断并发越高越快,并行度从 4 提到 16 后,大量请求进入重试循环,总时间反而成倍膨胀。带宽有余量,CPU 和内存也没有压力------瓶颈不在客户端。
测试环境(示例):
text
OS: Ubuntu 24.04 LTS
yt-dlp: 2024.12.23
ffmpeg: 7.1
高并发方案下的典型报错:
text
WARNING: [youtube] xxxxxxx: Retrying (attempt 1 of 5) ...
WARNING: Retrying (attempt 2 of 5) ...
ERROR: HTTP Error 429: Too Many Requests
还有一种更隐蔽的情况:服务端不返回 429,而是对每个 TCP 连接降速到 20-50 KB/s。客户端不报错,日志看起来一切正常。不设置 --throttled-rate 时,yt-dlp 对这种暗限速完全无感知。
以上报错模式基于 yt-dlp 仓库文档记录的重试与限速应对逻辑,为复现路径示例,非特定批次实测数据。
二、排查与选择依据
结论:格式选择器决定请求数量,进程数与 fragment 并发叠加决定瞬时连接数,合并阶段依赖上游完整性------三条线共同决定限速触发概率。逐条排查后才能定位瓶颈。
格式选择器
当选择器为 -f "137+140"(视频流 + 音频流分离)时,yt-dlp 需完成三个阶段:先拉 manifest,再分别下载视频流和音频流,最后调用 ffmpeg 合并。manifest 请求出现在流程最前面,在高并发下最先被限速。
先用 -F 查看可用格式:
bash
yt-dlp -F "https://www.youtube.com/watch?v=VIDEO_ID"
text
[info] VIDEO_ID: Available formats for quality:
ID EXT RESOLUTION | FILESIZE TBR | VCODEC | ACODEC
251 webm audio only | 4.23MiB 70k | audio only | opus
137 mp4 1080p | 56.00MiB 850k | avc1.640028 | video only
140 mp4 audio only | 5.60MiB 129k | audio only | mp4a.40.2
选 137+140 意味着两个流各走一路独立下载请求。在 -P 16 并发下,manifest、视频流、音频流三类请求同时膨胀,服务端看到的连接数是单流格式的两倍以上,限速触发概率成倍上升。改用单流格式(如 18),请求数减半,在同等并发下更不容易触发限速。
线程叠加
并发在批量场景中有两个独立来源,容易被混淆:
xargs -P N:同时运行 N 个 yt-dlp 进程,每个进程处理一个视频--concurrent-fragments M:单个进程内并发请求 M 个 fragment
这两个参数是相乘关系。实际瞬时连接数 = N × M。yt-dlp 仓库文档明确说明:
Number of concurrent fragments to download. The default is 1 for most extractors, 4 for HLS and DASH.
大多数提取器的默认值为 1,HLS/DASH 场景才升到 4。-P 16 + --concurrent-fragments 4 = 64 个并发连接,远超官方预期的安全范围。
分离流合并
两个流下载完成后,yt-dlp 调用 ffmpeg 做无损流复制合并:
bash
ffmpeg -i video.mp4 -i audio.m4a -c copy output.mp4
合并阶段不产生网络请求,CPU 开销也极低(流复制不做转码)。但如果某个流因 429 或暗限速下载不完整,ffmpeg 会在合并时报错:
text
[mp4 @ 0x...] Could not find tag for codec opus in stream #1
这类错误的根因在上游下载阶段的完整性不足,应先回头检查 yt-dlp 的下载日志,而不是反复调整 ffmpeg 参数。
限速触发机制
服务端按 IP 限速有两种表现形式:
text
16 进程 × 4 fragment = 64 连接
↓
服务端按来源 IP 识别异常流量
↓
方式 A:返回 HTTP 429(显式)
方式 B:每连接降速到 20-50 KB/s(暗限速)
↓
每个请求进入指数退避重试:5s → 10s → 20s → 40s → 80s
↓
总耗时 > 低并发方案
yt-dlp 提供两个直接应对参数:

--throttled-rate RATE:当检测到下载速率低于阈值时触发重试。这是目前唯一能发现暗限速的参数。--sleep-requests SECONDS:在每个请求之间插入固定等待,从客户端侧主动降低触发概率。
这两个参数的存在说明 yt-dlp 的设计者已预期到服务端会限速,并把控制权交给了用户。
替代方案与取舍
| 方案 | 适用条件 | 代价 | 边界 |
|---|---|---|---|
低并发 + --sleep-requests |
一次性归档,不追求极速 | 总耗时较长 | 长期高频场景吞吐不足 |
| aria2c 外部下载器 | 需要更精细的分段控制 | 需额外安装 aria2c | 部分站点对 UA 更敏感 |
--cookies-from-browser |
靠认证身份提高阈值 | 暴露登录态,有隐私风险 | 限速可能升级为账号级 |
代理池 + --proxy |
长期批量场景 | 需维护代理基础设施 | 代理不稳定时反而增加故障率 |
--download-archive |
分批断点重跑 | 需管理 archive 文件 | 已归档 ID 不会重新下载 |
--cookies-from-browser 的反直觉风险:暴露登录态后,限速维度可能从 IP 级升级为账号级。服务端一旦按账号识别异常,所有走该账号的请求同时失败,比单纯 IP 限速更难恢复。cookies 不应作为常规提速手段,只在账号本身有更高配额时才有意义。
不适合本文方案的情况:目标站点有持续验证码或强反自动化策略时,低并发节流无法解决根本问题;需要实时性时应换用官方 API,而不是在 HTTP 层面调参数。
三、可运行示例
输入 :1000 个视频 URL 写入 urls.txt,每行一个。
bash
echo "https://www.youtube.com/watch?v=VIDEO_ID_001" > urls.txt
echo "https://www.youtube.com/watch?v=VIDEO_ID_002" >> urls.txt
# ...共 1000 行
低并发节流方案(推荐起步值):
bash
xargs -a urls.txt -P 3 -I {} yt-dlp \
--concurrent-fragments 1 \
--sleep-requests 1 \
--retries 5 \
--fragment-retries 5 \
--throttled-rate 50K \
-f "137+140" \
-o "downloads/%(title)s.%(ext)s" \
{}
预期输出 :每个视频正常完成下载,日志中无 Retrying 或 429 记录,瞬时连接数不超过 3 个。
高并发方案(用于复现问题):
bash
xargs -a urls.txt -P 16 -I {} yt-dlp \
--concurrent-fragments 4 \
--retries 5 \
--fragment-retries 5 \
-f "137+140" \
-o "downloads/%(title)s.%(ext)s" \
{}
实际输出(复现路径示例,非特定批次实测数据):
text
WARNING: [youtube] xxxxxxx: Retrying (attempt 1 of 5) ...
WARNING: Retrying (attempt 2 of 5) ...
ERROR: HTTP Error 429: Too Many Requests
用 ss 监控瞬时连接数,验证并发叠加效应:
bash
watch -n 1 'ss -tn | grep -c ":443"'
常见失败及修复:
| 失败现象 | 原因 | 修复 |
|---|---|---|
HTTP Error 429 |
瞬时并发超过阈值 | 降低 -P 到 2-3,加 --sleep-requests 1 |
| 速率骤降且不报错 | 暗限速 | 加 --throttled-rate 50K 触发重试 |
| ffmpeg 合并报错 | 某个流下载不完整 | 加 --fragment-retries 10,必要时重跑单个视频 |
四、验证结果与边界
用 ffprobe 对比音视频流的 duration 差值,是验证合并结果是否正常的最低成本方法:
bash
ffprobe -v error -show_entries stream=index,codec_type,duration \
-of json "downloads/VIDEO_TITLE.mp4"
预期输出:
json
{
"streams": [
{ "index": 0, "codec_type": "video", "duration": "342.10" },
{ "index": 1, "codec_type": "audio", "duration": "342.08" }
]
}
两个流的 duration 差值应在 0.1 秒以内。超过 1 秒时说明合并出了问题,需重新下载该视频。批量验证可以用一行脚本扫过整个 downloads/ 目录,只输出差值异常的文件。
适用边界:
- 适合:一次性批量归档、离线素材收集,带宽有余量但服务端限速明显的场景
- 不适合:持续高频拉取、对单次下载延迟敏感、目标站点有强反自动化策略
- 版本注意 :旧版 youtube-dl 用
-N/--threads表示线程数,yt-dlp 对应选项为--concurrent-fragments,迁移时沿用旧参数可能被静默忽略。YouTube 等站点接口变更频繁,旧版 yt-dlp 可能因签名算法变化直接报错
思考
- 被限速后加
--cookies-from-browser,是否可能把 IP 级限制升级为账号级限制,得不偿失? - 代理池分散 IP 和低并发节流两条路径,长期运维成本哪条更低?