视频平台的数据库设计:从用户体系到弹幕系统的Schema架构复盘

发布时间:2026/9/15 3:15:21

视频平台的数据库设计:从用户体系到弹幕系统的Schema架构复盘 视频平台的数据库设计从用户体系到弹幕系统的Schema架构复盘一、背景与问题定义视频平台的数据库设计与传统业务系统有显著差异读多写少但写入峰值尖锐、冷热数据分化严重、以及弹幕这类高吞吐写入场景对数据库选型提出挑战。一个典型的千万 DAU 视频平台弹幕写入的峰值 QPS 可达 50 万以上远超出单机 MySQL 的承载能力。本文以用户—视频—互动三条核心业务线为骨架复盘整个平台的 Schema 设计、弹幕高吞吐写入方案、以及数据归档策略。二、核心业务 Schema 设计2.1 用户体系用户表的核心设计原则是高频查询字段与低频字段垂直拆分认证信息与基础信息隔离。-- 用户基础信息表高频读 CREATE TABLE user_base ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT 业务用户ID对外暴露, nickname VARCHAR(64) NOT NULL, avatar_url VARCHAR(512) DEFAULT , bio VARCHAR(256) DEFAULT COMMENT 个人简介, follower_count INT NOT NULL DEFAULT 0, following_count INT NOT NULL DEFAULT 0, video_count INT NOT NULL DEFAULT 0 COMMENT 发布视频数, total_likes BIGINT NOT NULL DEFAULT 0, creator_level TINYINT NOT NULL DEFAULT 0 COMMENT 创作者等级0-10, status TINYINT NOT NULL DEFAULT 1 COMMENT 1正常 2冻结 3注销, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_id (user_id), KEY idx_creator_level (creator_level, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 用户认证信息表低频访问安全隔离 CREATE TABLE user_auth ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, phone VARCHAR(32) DEFAULT COMMENT AES加密存储, email VARCHAR(128) DEFAULT , password_hash VARCHAR(256) NOT NULL, last_login_at DATETIME DEFAULT NULL, last_login_ip VARCHAR(64) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;垂直拆分的动机user_auth表仅在登录/注册时访问与user_base每页都查的模式完全不同。分开后user_auth可以放在加密存储卷上甚至使用独立的数据库实例。2.2 视频信息表CREATE TABLE video_info ( id BIGINT NOT NULL AUTO_INCREMENT, video_id BIGINT NOT NULL COMMENT 业务视频ID, user_id BIGINT NOT NULL, title VARCHAR(256) NOT NULL, description TEXT DEFAULT NULL, cover_url VARCHAR(512) DEFAULT , duration INT NOT NULL DEFAULT 0 COMMENT 视频时长秒, category_id INT NOT NULL DEFAULT 0, tags JSON DEFAULT NULL COMMENT AI生成的标签JSON数组, play_count BIGINT NOT NULL DEFAULT 0 COMMENT 播放次数, like_count INT NOT NULL DEFAULT 0, comment_count INT NOT NULL DEFAULT 0, share_count INT NOT NULL DEFAULT 0, barrage_count INT NOT NULL DEFAULT 0 COMMENT 弹幕总数, status TINYINT NOT NULL DEFAULT 0 COMMENT 0转码中 1正常 2审核中 3下架, audit_result JSON DEFAULT NULL COMMENT 多模态审核结果, publish_at DATETIME DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_video_id (video_id), KEY idx_user_status (user_id, status), KEY idx_category_publish (category_id, publish_at), KEY idx_play_count (status, play_count) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;关键设计决策计数器冗余play_count、like_count等计数字段直接冗余在视频表上。虽然违反了严格的规范化但避免了 SELECT COUNT(*) 的昂贵开销。计数器更新通过 Redis 原子操作 异步刷 MySQL。JSON 字段用于动态属性tagsAI 标签和audit_result审核结果使用 JSON 类型。这两个字段结构变化频繁——标签体系每季度迭代审核维度持续增加——JSON 的 Schema-less 特性避免了频繁 DDL。status 字段的状态机严格遵循 0→2→1 的流转转码→审核→正常不允许逆向流转审核不过直接到 3 下架。2.3 互动体系-- 评论表 CREATE TABLE comment ( id BIGINT NOT NULL AUTO_INCREMENT, comment_id BIGINT NOT NULL, video_id BIGINT NOT NULL, user_id BIGINT NOT NULL, parent_id BIGINT NOT NULL DEFAULT 0 COMMENT 0一级评论, reply_to_uid BIGINT NOT NULL DEFAULT 0 COMMENT 被回复者, content TEXT NOT NULL, like_count INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_comment_id (comment_id), KEY idx_video_created (video_id, created_at), KEY idx_parent (video_id, parent_id, created_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 点赞表只记录关系计数器在Redis CREATE TABLE like_record ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, target_type TINYINT NOT NULL COMMENT 1视频 2评论, target_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_target (user_id, target_type, target_id), KEY idx_target (target_type, target_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;三、弹幕高吞吐写入方案3.1 整体写入链路弹幕的写入链路遵循先广播后落盘的原则。用户发送弹幕后先写入 Redis保证实时广播同时投递到 Kafka保证持久化Kafka Consumer 批量写入 MySQL。3.2 Redis 实时存储Service public class BarrageWriteService { private final StringRedisTemplate redisTemplate; private final KafkaTemplateString, BarrageMessage kafkaTemplate; public void sendBarrage(BarrageMessage msg) { // 1. 写入 Redis实时查询用 String redisKey barrage:video: msg.getVideoId(); long score msg.getTimestamp(); // 视频时间戳作为score redisTemplate.opsForZSet().add(redisKey, JSON.toJSONString(msg), score); // 2. Redis ZSet 只保留最近 5000 条 redisTemplate.opsForZSet().removeRange(redisKey, 0, -5001); // 3. 异步投递到 Kafka 做持久化 kafkaTemplate.send(barrage-persist, String.valueOf(msg.getVideoId()), msg); // 4. 实时广播给同房间用户通过 WebSocket broadcastToRoom(msg.getVideoId(), msg); } }3.3 Kafka 批量写入 MySQLComponent public class BarragePersistConsumer { private static final int BATCH_SIZE 500; private static final int FLUSH_INTERVAL_MS 2000; private final ListBarrageMessage buffer new ArrayList(); private long lastFlushTime System.currentTimeMillis(); KafkaListener(topics barrage-persist, concurrency 3) public void onMessage(BarrageMessage msg) { synchronized (buffer) { buffer.add(msg); if (buffer.size() BATCH_SIZE || System.currentTimeMillis() - lastFlushTime FLUSH_INTERVAL_MS) { flushBuffer(); } } } private void flushBuffer() { if (buffer.isEmpty()) return; ListBarrageMessage batch; synchronized (buffer) { batch new ArrayList(buffer); buffer.clear(); lastFlushTime System.currentTimeMillis(); } // INSERT ... ON DUPLICATE KEY UPDATE 实现幂等 jdbcTemplate.batchUpdate( INSERT INTO barrage_{tableSuffix} (barrage_id, video_id, user_id, content, video_time, created_at) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE content VALUES(content), batch, BATCH_SIZE, (ps, msg) - { ps.setLong(1, msg.getBarrageId()); ps.setLong(2, msg.getVideoId()); ps.setLong(3, msg.getUserId()); ps.setString(4, msg.getContent()); ps.setDouble(5, msg.getVideoTime()); ps.setTimestamp(6, Timestamp.from(msg.getCreatedAt())); }); } }3.4 弹幕按月分表弹幕表按月分表barrage_202607、barrage_202608依据是弹幕的查询场景高度集中于当前视频对应的月份——用户看弹幕时绝大多数请求落在最近几周的视频。历史视频的弹幕查询量占比不到 2%。CREATE TABLE barrage_202607 ( id BIGINT NOT NULL AUTO_INCREMENT, barrage_id BIGINT NOT NULL, video_id BIGINT NOT NULL, user_id BIGINT NOT NULL, content VARCHAR(512) NOT NULL, video_time DOUBLE NOT NULL COMMENT 弹幕在视频中的时间位置秒, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME(3) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_barrage_id (barrage_id), KEY idx_video_time (video_id, video_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;四、Schema 设计原则总结4.1 反范式化计数器字段play_count、like_count直接冗余在主表上是典型的用存储换查询性能。一个视频详情页每次被访问都需要展示这些数字如果每次 SELECT COUNT(*) 从统计表实时计算在百万 QPS 的读压力下会直接击穿数据库。4.2 预留字段视频表的tags和audit_result使用 JSON 类型而非结构化字段就是在为未来的属性扩展预留空间。当 AI 团队说下个月我们要新增 3 个维度的标签时JSON 字段只需改代码逻辑不需要 DDL。4.3 归档策略数据分为热、温、冷三层热数据近 3 个月完整保留在 MySQL 主库读写均可。温数据3~12 个月保留在 MySQL 只读副本查询延迟略高但可接受。冷数据12 个月以上归档到对象存储Parquet 格式按需通过 Presto/Trino 查询不占用 MySQL 存储。弹幕的归档最激进3 个月以上的弹幕直接从 MySQL 迁移到对象存储前端播放时通过 CDN 边缘节点加载归档弹幕文件。五、总结视频平台的数据库设计围绕三个核心原则读写分离高频读字段垂直拆分、计数缓存到 Redis、冷热分离弹幕按月分表、3 个月归档、以及用存储换性能合理反范式化。弹幕的高吞吐写入通过Redis → Kafka → 批量 MySQL三级链路实现峰值写入从单机 MySQL 的 5000 QPS 提升到 50 万 QPS。后续优化方向引入 TiDB 替代部分按月分表的 MySQL 集群减少运维成本弹幕的查询链路引入 Redisearch 做全文检索支持在这部剧的第 5 集搜索所有红色弹幕以及冷数据查询的统一化构建 Iceberg Trino 的冷数据查询层。
延伸阅读

更多相关文章

2026/9/15 3:13:46

备受国自然评审青睐的蛋白互作研究利器:PLA 邻位连接技术

邻位连接技术(Proximity Ligation Assay,PLA)是一类整合抗体特异性识别与核酸信号放大能力的高精准蛋白质分析技术,无需基因修饰即可在单分子水平原位解析蛋白质相互作用、翻译后修饰事件,是蛋白功能机制研究中认可度极…

2026/9/12 23:31:25

Agent SaaS:从工具到劳动力的范式转变

1. Agent作为新一代SaaS的范式转移当Slang AI的餐厅预订系统能在电话铃响三声内完成90%的客户咨询时,传统SaaS的商业模式正在被重新定义。这不是简单的技术升级,而是从"工具提供商"到"劳动力供应商"的根本性转变。我最近帮三家本地餐…

2026/9/15 2:34:00

C 语言基础学习笔记(Ubuntu 64 位适配版)

前言本帖为嵌入式 Linux 方向 C 语言入门学习资料,结合计算机底层硬件原理、进制换算、内存存储规则与 C 语言基础数据类型展开讲解,适配 64 位 Ubuntu 虚拟机开发环境。内容从计算机硬件运行逻辑切入,补齐进制转换、数据存储底层原理&#x…

2026/9/15 3:11:29

火焰图像语义分割数据集:二分类、像素级标注与工业落地实践

简介:本资源是一套专为计算机视觉初学者与算法工程师设计的火焰图像语义分割数据集,聚焦工业安全、火灾监测等实际场景中的二分类分割任务。数据集严格遵循标准分割格式:原始图像(256256 JPG)与对应0/1二值掩膜&#x…

2026/9/15 3:11:29

Keil添加文件闪退原因排查与解决全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/15 3:11:29

C++台球游戏源码解析:物理模拟、碰撞检测与实战调试

简介:这是一份基于C开发的经典台球游戏完整工程,适合正在学习游戏编程、C面向对象设计或需要毕业设计参考的开发者使用。源码通过类与对象封装球台、球杆、台球等核心元素,并实现碰撞检测、物理模拟、事件处理、游戏循环等关键机制&#xff0…

2026/9/15 3:11:29

基于Hadoop+Spark的北京二手房大数据分析平台构建

1. 项目背景与核心价值北京二手房市场作为国内最具代表性的房地产市场之一,其数据具有典型的高维度、非线性和时空相关性特征。这个项目通过构建基于HadoopSpark的大数据处理分析平台,实现了对二手房市场的多维度特征挖掘与可视化呈现,为市场…

2026/9/15 3:06:29

Skills协议:可验证、可复用的能力建模与评分体系

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/15 0:01:16

AI英语单词APP开发:自适应学习算法与移动端优化实践

1. 项目概述 作为一名在移动应用开发领域摸爬滚打多年的老手,我最近完成了一个AI英语单词APP的开发项目。这个项目将传统单词记忆方法与现代AI技术相结合,打造了一款能够智能适应不同用户学习习惯的英语学习工具。 市面上大多数单词APP都存在一个通病&a…

2026/9/15 0:01:16

Flutter与OpenHarmony结合开发手语学习APP实战

1. 项目背景与核心价值作为一名同时接触过Flutter和OpenHarmony的开发者,最近我完成了一个基于Flutter for OpenHarmony的手语学习APP实战项目。这个项目最大的特点在于实现了跨平台框架与国产操作系统深度结合的创新实践——用Flutter开发的应用能完美运行在OpenHa…

2026/9/15 0:01:16

六个月成为机器人工程师:从ROS2到SLAM的实战路径

1. 六个月的紧迫感从哪来:先搞清楚你要成为哪种机器人工程师说实话,六个月的期限并不是一个宽松的时间线。市面上任何一本正经的机器人学教材都超过五百页,ROS2的官方文档可以翻到你怀疑人生,再加上ABB、KUKA这些工业机器人厂家动…

2026/9/14 11:59:31

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/14 11:22:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码