给一个 2 亿 DAU、峰值千万级并发播放的点播平台(Netflix、YouTube、Disney+)设计后端。聊天(Day 15)是连接放大,Feed(Day 14)是读放大,视频流则是带宽放大 + 计算放大的双重难题:单条 4K 流稳态约 15–25 Mbps,千万并发就是 百 Tbps 级出口带宽——比所有公网骨干能扛的还大,不可能从中心机房直接发。同时一部 2 小时电影要转出几十个码率/分辨率/编码格式的版本,是重 CPU/GPU 作业。难点不在「存视频」,而在怎么把同一内容用最省带宽的方式,在用户网络波动下流畅地推到全球每块屏幕上。
关键是把「上传转码」和「播放分发」两条路彻底分开。左侧是离线/异步的转码 pipeline:片源切成小块、并行编码成多码率梯队、打包成 HLS/DASH、写入对象存储——慢没关系,一次转码亿次播放。右侧是在线的播放路径:客户端先拿 manifest(清单),再按网络状况逐段拉 segment;99% 的字节由 CDN 边缘命中,源站只在边缘 miss 时回源。控制面(播放 API、DRM 发证、推荐)只传元数据,不碰视频字节。
左侧离线转码(一次),右侧在线播放(亿次);带宽全压在 CDN 边缘,源站只兜底
原理:同一片源必须转出一组 (分辨率, 码率, 编码) 组合,称为码率梯队(bitrate ladder),让播放器按网速挑。片源先按 shot/GOP 边界切成几秒一块的 chunk,扔到几百~几千台机器并行编码,再做质量校验(QC)和打包。关键洞察:不同内容的压缩难度天差地别——动画/纯色画面 1Mbps 就完美,体育/爆炸场面 8Mbps 还糊。用一刀切的固定梯队,简单内容浪费带宽、复杂内容质量不够。
| 方案 | 做法 | 代价 | 带宽收益 |
|---|---|---|---|
| 固定梯队 | 所有片用同一组码率(如 235k→5800k 固定档) | 简单、转码快 | 基线 |
| Per-Title | 按整片复杂度选最优梯队(凸包 convex hull) | 需先做复杂度分析 | ↓~20%+ |
| Per-Shot | 逐镜头选分辨率/QP,VMAF 最优 | 编码单元×10,算力暴涨 | ↓~30% |
# 并行分块转码(伪代码)
chunks = split_by_shot(source) # 按镜头/GOP 边界切,保证可独立解码
ladder = per_title_ladder(source) # 复杂度分析 → 凸包选码率档
for c in chunks: # fan-out 到数百 worker
for rung in ladder:
enqueue(encode_task(c, rung)) # (chunk, 分辨率, 码率, codec)
# 全部完成后 reduce:拼接 + QC(VMAF 抽检) + 打包 HLS/DASH
assemble_and_package(wait_all()) # 任一 chunk 失败只需重跑那一块
原理:网络时刻波动,播放器必须逐段决定下一段拉哪个码率,在「画质高」和「不卡顿」之间实时权衡。这是 客户端 的决策(服务端只提供多档 segment)。两大流派:throughput-based 用最近几段的下载速度预测下一段该选多高;buffer-based(如 BOLA)只看当前缓冲水位——缓冲多就升档、缓冲少就降档。现代播放器是混合:起播阶段缓冲空,靠 throughput 快速试探;稳态靠 buffer 抗抖动。
# 混合 ABR 选档(简化伪代码,每段决策一次)
def pick_bitrate(buf_sec, recent_bw, ladder):
safe_bw = 0.85 * ema(recent_bw) # 留安全余量,防抖
cap = max(r for r in ladder if r <= safe_bw)
if buf_sec < LOW: return min(ladder) # 缓冲告急 → 立刻保底,宁糊不卡
if buf_sec > HIGH: return up_one(cap) # 缓冲充足 → 大胆升档
return cap # 稳态跟随带宽
原理:视频 99%+ 的字节必须由离用户最近的边缘节点发出,源站只兜底。难点是命中率和填充策略:热门新剧上线,与其等用户 miss 再回源(瞬间打爆源站),不如提前 push 预热到边缘。冷门长尾内容则 pull-on-demand。Netflix 走到极致——把缓存服务器(Open Connect Appliance)直接放进 ISP 机房,视频根本不走公网骨干,ISP 和 Netflix 双赢(省 ISP 的 transit 费、给用户更快)。
原理:起播时间(TTFF)= DNS/TLS + 取 manifest + 拿 DRM 证 + 下载并解码第一个 segment。要压到 2s 内,每一步都要抠:连接预建(warm CDN connection)、manifest 瘦身、首段用低码率快速起播(先放出来再悄悄升档)、关键帧前置。Segment 时长是核心旋钮:短(2s)切换灵敏、起播快,但请求数多、编码效率低(关键帧密);长(6–10s)效率高但 ABR 反应钝、起播慢。点播常用 4–6s 折中。
高频追问:① 估算千万并发的出口带宽与一部电影的转码/存储成本量级。② 一段视频怎么从上传走到能在全球播放?画出 pipeline。③ ABR throughput-based 与 buffer-based 各自失效场景,怎么混合?④ 新剧首发如何避免回源风暴?⑤ 点播 vs 直播架构差异在哪(转码实时性、延迟、缓冲策略)?
1000 万 × 4 Mbps = 4×10⁷ Mbps = 40 Tbps(峰值 4K 更多,可达上百 Tbps)。对比:单个超大数据中心的总出口通常也就几 Tbps 到十几 Tbps 量级;全球海缆某条干线也就 Tbps 级。40 Tbps 意味着你需要把流量摊到全球成百上千个边缘点,每个点扛几十~几百 Gbps,没有任何单一中心能聚合这么大出口。这就是为什么 CDN 是架构前提而非优化——而且按第三方 CDN 的 GB 单价,这个量级的带宽费会吞掉利润,逼出 Netflix 自建 Open Connect 的经济动机。算成本时记住:转码是一次性 CPU,带宽是永久性月费,所以宁可多花算力换永久带宽。
关键是转码成本 vs 带宽节省的摊销比。Netflix 一部热剧会被播放上亿次,per-shot 省 30% 码率 = 永久省 30% 的海量带宽费,一次性多花的编码算力被亿次播放摊到可忽略。创业公司两点不同:① 播放量低,省下的带宽绝对值小,摊不平额外编码成本;② 工程复杂度——per-shot 要先跑分析编码、维护凸包、海量编码单元的调度/重试/QC,是个专门团队的活。所以理性顺序是:先固定梯队跑起来 → 量大了上 per-title(性价比拐点)→ 头部内容才上 per-shot。这也是典型的「过早优化」反例:在 PMF 之前烧钱做 per-shot,省的带宽还不够付工程师工资。
纯 throughput:骤降瞬间它仍按「上一段 50M」选了高码率,这一段下不完→缓冲耗尽→卡顿;恢复后又激进升档,再遇抖动再卡,画质忽高忽低。纯 buffer-based(BOLA):只看缓冲水位,骤降时缓冲开始掉它才反应、平滑降档,抗抖更好;但代价是反应滞后,且起播阶段缓冲从 0 起、没历史信息,决策保守、起播慢。混合取两者长:起播阶段缓冲空,用 throughput 快速试探把首屏拉起来;稳态切到 buffer-based 抗抖动;并对带宽估计留安全余量(如 ×0.85)防止贴边导致频繁卡。本质是「不同阶段信息量不同,用当下最可靠的信号」。
纯 pull:新剧的 segment 还没在任何边缘节点,几百万请求同时 miss → 全部回源 → 源站和回源链路被瞬间打爆(这是缓存版的 thundering herd / 雪崩,见 Day 2)。结果:起播超时、大面积播放失败,正好在最该稳的时刻崩。防法:① Push 预热——已知会火,上线前夜间低谷把全部码率版本主动预填到全球边缘(Netflix Open Connect 的核心打法);② 回源加 single-flight,同一 segment 只放一个回源请求,其余等待;③ 边缘分层,区域中间层先扛一道再到源站,收敛回源扇出;④ 上线分批放量或预告倒计时打散点击峰值。核心思想和缓存预热一致:可预测的热点要主动填,别等 miss。
矛盾在于:缓冲是抗抖动的弹药,但缓冲越深、离直播现场越远。传统 HLS 要等一个完整 segment(如 6s)生成并写入才能被拉取,光这一步就累积 segment 时长的延迟。LL-HLS 的破法:把 segment 再切成 partial segment(部分段,几百毫秒),编码器边编边发、CDN 用 chunked transfer 边收边转发,播放器不必等整段完成就能拿到最新小块——延迟从「整段」降到「小块」级。同时用 preload hint / blocking playlist reload 让播放器精准预取下一小块、避免轮询空转。关键妙处是它仍是 HTTP + HLS 框架:复用现有 CDN 缓存、复用 ABR 多码率机制,不像 WebRTC 那样另起一套不可缓存的实时通道。代价是请求更频繁、对 CDN 的 chunked transfer 支持有要求,且缓冲薄了抗抖动余量变小——本质还是在「延迟 vs 流畅」上挪了个新平衡点,而非消灭矛盾。