视频平台架构决策:从存储到转码的选型逻辑
架构决策没有最优解,只有适合当前阶段的方案。
一、对象存储:MinIO vs 公有云 OSS
| 对比项 | MinIO(自建) | 公有云 OSS |
|---|---|---|
| 成本 | 服务器硬盘 | 按流量付费 |
| 上传速度 | 局域网 1Gbps | 家庭上行 10Mbps |
| 访问延迟 | <1ms(内网) | 50-200ms(公网) |
| 维护成本 | 自己搭、自己管 | 零维护 |
决策:选 MinIO
原因:家庭集群所有设备在局域网内。上传 64MB 视频到 MinIO 只需 0.5 秒,到公有云 OSS 要 30 秒(10Mbps 上行限制)。
代价是外网访问要走内网穿透,延迟较高。但对于以局域网用户为主的演示场景,这个代价可以接受。
二、数据库:PostgreSQL vs MySQL
| 对比项 | PostgreSQL | MySQL |
|---|---|---|
| 数据类型 | 丰富(JSONB、数组) | 基础类型 |
| 扩展生态 | pgvector、PostGIS | 有限 |
| 约束支持 | 完整(外键/检查/排他) | InnoDB 支持外键 |
| 并发模型 | MVCC 快照隔离 | MVCC RR 级别 |
决策:选 PostgreSQL
关键原因是用了pgvector做向量搜索,这是 PG 独有的扩展:
sql
CREATE EXTENSION vector; CREATE TABLE video_embeddings ( video_id INTEGER, embedding vector(768) ); -- 向量相似度检索 SELECT * FROM video_embeddings ORDER BY embedding <-> '[0.1, 0.2, ...]' LIMIT 5;
三、转码策略:多清晰度 vs 单清晰度
传统做法转三档:480p、720p、1080p,自适应切换。
决策:单档 720p,CRF37+ultrafast
原因:源文件码率已经极低(85-596kbps),是高度压缩的产物。转三档相当于“用不同分辨率展示同样的模糊”,体积翻三倍但画质没有实际提升。
| 方案 | 体积 | 存储 | 画质 |
|---|---|---|---|
| 三档标准码率 | 3x | 3x | 无明显提升 |
| 单档 CRF37 | 1x | 1x | 与源一致 |
核心原则:源文件糊了,转码只是把糊的放大。用 CRF 匹配源码率,而不是用固定码率强行转档。
四、流协议:HLS vs DASH
| 对比项 | HLS | DASH |
|---|---|---|
| 容器 | MPEG-TS | fMP4 |
| 原生支持 | Safari | Chrome/Firefox |
| 播放库 | hls.js | dash.js |
| 切片格式 | .ts | .m4s |
决策:选 HLS
hls.js 兼容性好,所有浏览器都能播。dash.js 对非标准 fMP4 解析有问题,而 m3u8+ts 是更成熟稳定的方案。
五、播放方式:Range 代理 vs 完整下载
| 方案 | 带宽消耗 | 实现复杂度 |
|---|---|---|
| 完整下载 | 每次全量 | 低 |
| Range 流式 | 按需传输 | 中 |
| 本地缓存 + Range | 首次后为 0 | 中高 |
决策:Range 代理 + 本地缓存
首次播放通过 Range 请求从存储流式转发,同时落盘缓存;再次播放直接从本地缓存读取,带宽消耗为 0。
六、编码参数:CRF vs CBR
| 对比项 | CBR(恒定码率) | CRF(恒定质量) |
|---|---|---|
| 码率 | 固定 | 自适应 |
| 体积 | 可控 | 不可控 |
| 画质 | 复杂场景不足 | 均匀 |
| 适用场景 | 直播推流 | VOD 点播 |
决策:CRF37
源文件本身码率极低,CBR 用标准码率转码只会膨胀体积。CRF37 自适应匹配源码率,输出体积与源基本一致。
七、上传事务性
问题:MinIO 有视频文件,数据库记录不全——上传文件后没有正确写入 DB。
解决方案:
python
# 上传后发消息异步写 DB minio_client.put_object(bucket, key, file) message_queue.publish("video.uploaded", {"key": key}) # 消费者保证写入 def on_video_uploaded(msg): try: db.execute("INSERT INTO videos (key, ...) VALUES (...)") except Exception: # 补偿:清理残留 minio_client.remove_object(bucket, msg["key"]) raise用补偿事务或消息队列确保存储和数据库最终一致。
八、总结
| 决策点 | 选择 | 理由 |
|---|---|---|
| 对象存储 | MinIO | 内网传输快、零成本 |
| 数据库 | PostgreSQL | 需要 pgvector 向量扩展 |
| 转码档位 | 单档 720p | 源已压糊,多档无意义 |
| 流协议 | HLS | hls.js 兼容性好 |
| 播放方式 | Range + 缓存 | 省带宽、体验好 |
| 编码参数 | CRF37 | 匹配源码率,不浪费空间 |
核心原则:架构选型没有标准答案,只有适合当前阶段的方案。家庭集群 + 有限上行带宽,本地存储 + 单档转码是合理的折中。等带宽和用户量上来了,再切换到更标准的方案。
