像 YouTube 或 Netflix 这样的视频流平台必须处理海量存储(每天数百 TB 上传)、复杂的多格式转码、最小缓冲的全球 CDN 投递,以及让用户保持参与的 ML 驱动推荐。上传管道和播放管道实际是两个有很不同约束的独立系统——上传是吞吐优化的批处理问题;播放是延迟优化的读路径问题。把这两者间的边界做对是核心设计挑战。

⚡ 速览要点
  • 点播用 TCP 而非 UDP——重传消除丢包伪影;UDP 主导延迟胜过质量的直播。
  • 经 Kafka 的上传管道——原始视频扇出到内容过滤、分类和多分辨率转码并行,再落进 blob 存储。
  • HLS/DASH 做 ABR——视频被切成多码率的 2–10 秒段;客户端 ABR 算法实时挑匹配可用带宽的质量层。
  • Blob 存储(S3)+ Cassandra——二进制文件在对象存储;视频元数据(按 ID 的固定查询模式)在 Cassandra 求高写吞吐。
  • CDN + 惰性段加载——只服务正在观看的段;大幅削减带宽和启动延迟。
  • 推荐离线——从 Kafka 缓冲的观看信号预计算;从键值存储 O(1) 查找服务。
  • 认证用 Redis cache-aside——首次未命中时加载;懒加载避免缓存从不被请求的数据。
  • 播放认证用签名 URL——CDN URL 里的限时 token 防盗链并强制访问控制而不碰视频字节。
tldr

视频上传流经一个 Kafka 支撑的内容过滤、分类和多分辨率转码管道,再落进 blob 存储和 Cassandra 元数据。转码段用 HLS/DASH 自适应码率流从地理分布的 CDN 服务。推荐离线计算并存在键值 NoSQL。TCP 被选过 UDP 求可靠——无丢包意味无缓冲伪影。

视频流平台高层架构
视频流平台高层架构

第 1 步 — 澄清需求

视频流覆盖很广的面。面试中显式界定以免花 45 分钟在一个你做不完的系统上。

功能

非功能

第 2 步 — 容量估算

假设一个 YouTube 量级的大平台:1000 万日活(DAU),每个每天看 ~10 个视频;1% 是内容创作者,各每天上传 ~2 个视频。

上传量

播放量

存储增长

第 3 步 — API 设计

两个不同的 API 面:上传 API(写多、异步处理)和播放 API(读多、延迟敏感)。

HTTP
# 上传:发起一个可恢复上传
POST /api/videos/upload/init
     { title, description, file_size_bytes, content_type }
     → { upload_id, upload_url }(预签名 S3 URL)

# 上传:上传块(可恢复、幂等)
PUT  {upload_url}
     Content-Range: bytes 0-5242879/104857600
     → 308 Resume Incomplete | 200 OK(最后一块)

# 检查处理状态
GET  /api/videos/{video_id}/status
     → { status: "processing"|"ready"|"failed", progress_pct }

# 播放:获取视频 manifest
GET  /api/videos/{video_id}/manifest
     → { hls_url, dash_url, thumbnail_url, duration_s, title }

# 播放:HLS 主播放列表(从 CDN 服务)
GET  https://cdn.example.com/v/{video_id}/master.m3u8
     → 列出所有质量变体带签名 URL 的 HLS 播放列表

# 观看历史 / 续播
POST /api/videos/{video_id}/progress
     { user_id, position_s, event: "pause"|"seek"|"complete" }

# 首页 feed
GET  /api/feed?user_id=123&page_token=abc
     → { videos: [...], next_page_token }

两个值得指出的设计选择:上传用可恢复分块上传(等价于 Google 的可恢复上传协议或 AWS S3 multipart 上传),使不稳定连接的移动创作者能在上传中途恢复而不重启。播放在 manifest 里返回签名 CDN URL——视频字节直接从 CDN 边缘节点服务,从不经应用服务器。

第 4 步 — 协议:TCP vs UDP vs QUIC

YouTube 和 Netflix 采用 TCP 作传输协议主要为保证视频质量——TCP 的重传确保零丢包,消除丢失 UDP 包造成的像素化或块状伪影。安全是次要好处。取舍是相比 UDP 略高的延迟,对点播流可接受,因为 2–10 秒缓冲吸收抖动。

协议延迟可靠性用例
TCP中等保证投递、有序点播 VOD 流——质量胜过延迟
UDP尽力而为、可能丢包直播、视频通话——延迟胜过质量
QUIC低(0-RTT)UDP 上可靠、多路复用流新兴:YouTube 用它在有损网络降低缓冲
WebRTC极低基于 UDP、浏览器原生实时 1:1 或小组视频(Zoom、Meets)

Google 的 QUIC 协议值得一提:它在 UDP 上实现类 TCP 可靠性、消除跨流的 TCP 队头阻塞,并支持 0-RTT 连接恢复。YouTube 一直在迁移到 QUIC 并报告可测量的缓冲事件减少,尤其在频繁 IP 地址变化(WiFi 和蜂窝间切换)的移动网络。

视频上传管道流程
视频上传管道流程

第 5 步 — 上传管道

上传路径是系统里最复杂的流程。原始视频文件在准备好供观众观看前经过多个转换。关键设计原则是经消息队列扇出:原始文件一落进暂存存储,一个 Kafka 事件就并行触发所有下游处理步骤,使内容过滤、分类和转码全部并发运行而非顺序。

flow
创作者上传原始视频(分块、可恢复)
  → 内容入驻初始化服务
      - 校验文件完整性(校验和)
      - 写 PENDING 记录到 Cassandra
      - 把原始文件移到暂存 S3 桶
  → 发布 VideoUploaded 事件到 Kafka
      ├── Multi-part Resolver: 把文件切成 ~5 分钟 GOP 段
      ├── 内容过滤: 筛查非法/不当材料
      │     └── 违规时: 标 REJECTED,通知创作者
      ├── 分类与打标: 生成标签、分类、语言
      │     └── ML 模型: 视觉 + 音频 + 转录信号
      └── 转码器(并行 worker):
            ├── 360p  H.264  ~400 Kbps
            ├── 480p  H.264  ~800 Kbps
            ├── 720p  H.264  ~2.5 Mbps
            ├── 1080p H.264  ~5 Mbps
            └── 4K   HEVC   ~15 Mbps
                  └── 每个分辨率 → S3 里的段文件
                        └── 内容服务写元数据到 Cassandra
                              → Elasticsearch 索引
                                    → 状态: READY

Multi-part Resolver 把上传切成并行部分,大幅减少大文件的总处理时间。每个 GOP(Group of Pictures)段能在一群转码 worker 上独立并行转码。一部顺序转码要 6 小时的 2 小时电影,有足够并行 worker 能在 30 分钟内完成——对 YouTube 承诺上传后近乎即时可用至关重要。

可恢复上传实现

移动网络上,500 MB 上传几乎肯定会被打断。可恢复上传协议用来自 S3 的预签名块 URL,并在一个 Redis 位图(按块号索引)里跟踪哪些块已确认。重连时,客户端查哪些块已收到并从第一个缺失块恢复。服务端分块状态存在 Redis 带 24 小时 TTL——之后未完成上传被放弃。

第 6 步 — 转码与 ABR 流

转码是管道里最资源密集的步骤。目标是产生每个视频的多个质量表示,切成短段,为自适应码率(ABR)投递格式化。

HLS vs DASH

协议段格式Manifest支持
HLS (HTTP Live Streaming)MPEG-TS 或 fMP4.m3u8 播放列表iOS/macOS/Safari 原生;广泛支持
DASH (Dynamic Adaptive Streaming)MP4 片段(fMP4)MPD XML manifestAndroid、Chrome、智能 TV;iOS 不原生

实践中,大平台转码成 HLS 和 DASH 两者以覆盖所有客户端类型。段文件本身(fMP4 块)常能在 HLS 和 DASH 间共享,因为两者都支持碎片化 MP4——只有 manifest 文件不同。段时长通常 2–6 秒:更短的段允许更快质量切换但增加 manifest 开销和每分钟视频的 HTTP 请求数。

ABR 算法

客户端播放器实现一个 ABR(自适应码率)算法,持续监控可用带宽和缓冲占用以选下一个要取的最优质量段。两个主导方法:

pseudocode
-- 基于缓冲的 ABR(简化)
FUNCTION select_next_quality(buffer_s, available_bitrates):
  IF buffer_s > 30:
    RETURN step_up(current_quality, available_bitrates)
  ELIF buffer_s < 10:
    RETURN step_down(current_quality, available_bitrates)
  ELSE:
    RETURN current_quality  -- 稳定区,保持当前

-- 段取循环
WHILE playing:
  quality = select_next_quality(buffer.seconds_remaining(), bitrates)
  segment_url = manifest.segment_url(next_segment_idx, quality)
  fetch_async(segment_url)  -- 从 CDN 边缘
  next_segment_idx += 1
转码管道和多分辨率输出
转码管道和多分辨率输出

第 7 步 — 数据模型与存储架构

存储选择由访问模式驱动。视频流有一个特别鲜明的拆分:二进制视频段属于对象存储;结构化元数据属于数据库。

数据类型存储理由
用户账户、计费SQL (RDBMS)认证和金融数据的 ACID 合规
视频元数据Cassandra (NoSQL)高写吞吐;固定查询模式(按视频 ID、按创作者)
视频段Blob 存储 (S3/GCS)为 PB 级大二进制内容专建的对象存储
推荐键值 NoSQL (DynamoDB/Redis)按用户 ID 快速点查;经离线批作业更新
搜索索引Elasticsearch带 type-ahead 的全文和模糊搜索
观看进度Cassandra高写吞吐(持续位置更新);按 (user_id, video_id) 为键
上传块状态Redis (TTL 24h)临时、快速的位图跟踪哪些块已确认

视频元数据的 Cassandra 数据模型

CQL
CREATE TABLE videos (
  video_id      UUID PRIMARY KEY,
  creator_id    UUID,
  title         TEXT,
  description   TEXT,
  status        TEXT,          -- PENDING|PROCESSING|READY|FAILED
  duration_s    INT,
  s3_prefix     TEXT,          -- S3 里所有段的基础路径
  hls_manifest  TEXT,          -- 到 master.m3u8 的 CDN URL
  dash_manifest TEXT,          -- 到 manifest.mpd 的 CDN URL
  thumbnail_url TEXT,
  tags          LIST<TEXT>,
  created_at    TIMESTAMP,
  updated_at    TIMESTAMP
);

-- 观看进度: (user, video) → 最后已知位置
CREATE TABLE watch_progress (
  user_id       UUID,
  video_id      UUID,
  position_s    INT,
  updated_at    TIMESTAMP,
  PRIMARY KEY   (user_id, video_id)
);

第 8 步 — 用户认证

认证流程用一个由 SQL 数据库支撑求 ACID 保证的标准 RESTful API 服务。一个带 Cache-Aside(懒加载)模式的 Redis 缓存坐在 SQL 数据库前:数据在首次未命中时加载进缓存、后续读从缓存服务。这里懒加载优于 write-through,因为它避免缓存从不实际被请求的数据——大多数用户账户在注册后从不被查;只有活跃会话 token 对热路径要紧。

会话 token 是短命 JWT(15 分钟访问 token + 7 天刷新 token)。token 刷新时,刷新 token 对照 Redis(活跃刷新 token 存那)校验,并签发一个新访问 token 而不碰 SQL 数据库。这让认证热路径完全在 Redis 里。

第 9 步 — 内容投递与播放授权

视频文件经一个全球分布的 CDN 网络服务。一个 Host Optimizer 服务基于地理邻近和当前服务器负载把每个客户端导向最近的 CDN 节点。CDN 层应用惰性段加载——只取用户当前观看的段,而非完整视频文件,减少带宽成本和启动延迟。

用签名 URL 做播放授权

视频播放的访问控制不能依赖 CDN 在每个段请求检查 token——那会饱和应用服务器。相反,解法是 CDN 签名 URL:

  1. 客户端向应用服务器认证并请求一个视频 manifest。
  2. 应用服务器生成一个短命签名 token(用 CDN 共享 secret 签名)、把它嵌进 manifest 的段 URL,并返回 manifest。
  3. 客户端直接从 CDN 取段。CDN 验证签名而无任何应用服务器参与——热路径上零开销。
  4. token ~1 小时后过期,防止盗链并强制订阅有效性。
pseudocode
-- 服务端:为一个段生成签名 URL
FUNCTION sign_cdn_url(base_url, user_id, expires_at, secret):
  policy = {
    "url": base_url,
    "user_id": user_id,
    "exp": expires_at
  }
  signature = hmac_sha256(json_encode(policy), secret)
  RETURN base_url + "?token=" + base64url(policy) + "&sig=" + signature

-- CDN 边缘:对每个段请求验证
FUNCTION validate_cdn_request(request, secret):
  policy, sig = parse_token(request.query.token, request.query.sig)
  IF policy.exp < now():  RETURN 401
  expected = hmac_sha256(json_encode(policy), secret)
  IF sig != expected:       RETURN 403
  RETURN 200               -- 服务该段

第 10 步 — 首页、搜索与推荐

首页结合两个数据源:

推荐架构

推荐从观看历史、观看时长和交互信号(点赞、分享、重看、跳过)离线计算。这些信号从播放服务喂进 Kafka 并用于批量训练推荐模型。架构有两个组件:

每个用户的预计算推荐存在 DynamoDB,按 user_id 为键,由离线批管道每 1–6 小时刷新。这在服务时给 O(1) 查找,关键首页路径上无推理延迟。

第 11 步 — 播放跟踪

播放期间,客户端持续记录观看状态和位置。这数据流入 Kafka 并驱动多个下游消费者:

第 12 步 — 扩展、容错与直播

扩展转码管道

转码 CPU 密集且自然可并行。在每天 100 TB 摄取,转码机群必须弹性:

CDN 容错

直播 vs VOD

维度VOD(点播)直播
协议TCP (HLS/DASH)UDP 或 QUIC(超低延迟用 WebRTC)
段可用性所有段预生成段实时生成;manifest 滚动
CDN 缓存高缓存命中率(同样段服务多次)每个段唯一且几分钟后过期
转码离线、并行、可重试实时、单遍、必须在段截止前完成
延迟目标2–10 秒启动 OK体育/活动 <3 秒;标准直播 <30 秒

对直播,转码路径必须以严格截止实时处理每个 GOP 段。一个专用实时转码集群(区别于 VOD 机群)处理直播流,用更低延迟编码预设(更快但质量略低)以满足截止。滚动 manifest 是一个特殊 HLS/DASH 变体(低延迟 HLS),每 0.5–2 秒发布新段而非标准的 2–6 秒。

关键取舍

决策选择接受的取舍
传输协议(VOD)TCP (HLS/DASH)比 UDP 更高延迟;可接受因为 2–10s 缓冲吸收抖动
推荐离线预计算推荐 1–6 小时陈旧;抓住大多数行为变化
视频元数据存储Cassandra无关系 join;查询模式必须在 schema 设计时已知
ABR 段长4–6 秒更短 = 更快质量切换但更多 HTTP 请求和 manifest 开销
播放认证CDN 签名 URLtoken 过期意味很长会话上短暂重认证;应用服务器不在热路径
转码并行GOP 并行 worker必须确保 GOP 边界对齐;相比每文件顺序略有开销
总结

视频流是两个系统:上传(为吞吐、正确性和并行处理优化的复杂多阶段管道)和播放(经 CDN、ABR 和惰性段加载为延迟优化)。TCP 的无丢包保证对点播流值得那个延迟取舍。推荐属于离线——预计算一次、从键值存储快速服务、从 Kafka 缓冲的行为信号持续重训。CDN 签名 URL 是强制播放授权而不让应用服务器在热路径上的正确方式。

🎯 面试速答

点播视频为什么 TCP 而非 UDP?TCP 重传消除丢包伪影(像素化、块状);略增的延迟对缓冲的点播内容可接受,因为播放器维护 2–10 秒缓冲。UDP 对低延迟比完美质量更要紧的直播更受偏好。
为什么把推荐存在键值存储而非即时计算?推荐模型运行昂贵;离线预计算并按用户 ID 从键值存储服务给 O(1) 查找,关键路径上无推理延迟。
为什么视频元数据用 Cassandra?查询固定模式(按视频 ID、按创作者)、写吞吐高(状态更新、观看计数自增),且无需关系 join——Cassandra 的宽列模型理想。
ABR 怎么工作?视频被切成多码率的 2–6 秒段。客户端 ABR 算法监控缓冲占用和测量吞吐,选能无重缓冲下载的最高质量层。
怎么授权视频播放而不过载应用服务器?用嵌在 manifest 里的 CDN 签名 URL。CDN 在边缘验证 HMAC 签名而无任何应用服务器参与——认证在 manifest 取时发生一次,而非每个段。

← 上一篇
设计地图 Web 应用