mysql在在线直播系统中的用户和频道表设计

来源:这里教程网 时间:2026-02-28 20:52:52 作者:

用户表要存什么字段,别一上来就加头像和昵称

在线直播系统里,

user
表核心是身份认证和基础状态,不是社交平台。很多团队早期把
avatar
introduction
全塞进去,结果后续做读写分离或分库分表时字段膨胀拖慢主键查询。

id
用 BIGINT UNSIGNED AUTO_INCREMENT(别用 UUID,索引碎片高,JOIN 慢)
必须有
account_type
(如
'phone'
'wechat_mini'
'guest'
),区分登录来源,避免后期硬编码判断
status
建议用 tinyint:0=禁用,1=正常,2=待实名,别用字符串枚举——MySQL 8.0+ 虽支持 CHECK,但 ORM 层解析易出错
密码字段叫
password_hash
,不是
password
;留空
salt
字段(bcrypt/scrypt 不需要单独存 salt)
删掉
created_at
的 DEFAULT CURRENT_TIMESTAMP —— 如果用多机房部署,时钟不同步会导致主从延迟误判

频道表的 status 和 live_status 必须拆成两个字段

常见错误是只设一个

status
字段,用 0/1/2/3 表示“未开播/直播中/已下播/已封禁”,这会导致业务逻辑耦合严重:比如运营后台要查“所有可进入的频道”,得排除封禁 + 下播 + 未开播三种状态,SQL 写起来绕,缓存也难命中。

status
:生命周期状态,只管“这个频道还能不能被创建/编辑/删除”,值域为
0=deleted
1=active
(软删除必备)
live_status
:实时状态,只在直播服务推流时更新,值域为
0=idle
1=live
2=ending
(正在断流中),用 Redis + MySQL 双写,不走事务
last_live_time
字段必须有,类型 DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00',用于排序“最近开播的频道”,避免每次查
live_log
表关联
别给
channel
表加
cover_url
—— 封面图属于媒体元数据,应独立成
media_asset
表,用
asset_type='cover'
+
ref_type='channel'
+
ref_id
关联

用户和频道的关系不能只靠外键硬连

直播系统里,“谁创建了频道”“谁正在播”“谁关注了频道”“谁被禁言”是四类完全不同的关系,强行塞进一张

user_channel_relation
表,加七八个 flag 字段,不出三个月就会出现“查某用户所有关注频道超时”的 case。

创建关系走
channel.owner_id
(NOT NULL,直接外键到
user.id
),这是强一致性要求
主播关系用独立表
channel_streamer
:含
channel_id
user_id
role
(1=主讲,2=助播)、
joined_at
,支持一个频道多个主播
关注关系必须异步化:
follow
表只存
follower_id
followee_id
created_at
,查“我关注的频道”时走
SELECT c.* FROM follow f JOIN channel c ON f.followee_id = c.id WHERE f.follower_id = ? AND c.status = 1
,别预聚合
禁言等临时状态别进 MySQL —— 存 Redis Hash,key 为
channel:<channel_id>:ban_list</channel_id>
,field 是
user_id
,value 是
expire_ts
,应用层判断是否过期

时间字段统一用 DATETIME(3),别信“用 INT 存秒级时间戳更省空间”

有些老架构师坚持用

INT UNSIGNED
存 Unix 时间戳,理由是“节省 4 字节”。但在直播场景下,你得频繁做
BETWEEN
查询、按小时统计、与 NOW() 对比,MySQL 对 INT 时间戳无法使用索引范围扫描(除非加函数索引),反而更慢。

所有时间字段:created_at、updated_at、started_at、ended_at,全用
DATETIME(3)
(毫秒精度,适配 WebRTC 日志对齐)
建表时显式声明
DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
,避免应用层拼 SQL 时漏传
如果真要跨时区显示(比如主播在东京开播,观众在纽约看),不要在 DB 层转时区 —— 存 UTC,应用层按
timezone
字段(存在
user
表里)做格式化
别给时间字段加索引就完事:对
channel
表,高频查询是 “status=1 AND live_status=1 ORDER BY last_live_time DESC LIMIT 20”,所以复合索引要建
INDEX idx_status_live_last (status, live_status, last_live_time)
CREATE TABLE `user` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `account_type` varchar(20) NOT NULL,
  `account_id` varchar(64) NOT NULL,
  `password_hash` varchar(255) DEFAULT NULL,
  `status` tinyint NOT NULL DEFAULT '1',
  `created_at` datetime(3) NOT NULL,
  `updated_at` datetime(3) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_account` (`account_type`,`account_id`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
<p>CREATE TABLE <code>channel</code> (
<code>id</code> bigint unsigned NOT NULL AUTO_INCREMENT,
<code>owner_id</code> bigint unsigned NOT NULL,
<code>title</code> varchar(100) NOT NULL,
<code>status</code> tinyint NOT NULL DEFAULT '1',
<code>live_status</code> tinyint NOT NULL DEFAULT '0',
<code>last_live_time</code> datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
<code>created_at</code> datetime(3) NOT NULL,
<code>updated_at</code> datetime(3) NOT NULL,
PRIMARY KEY (<code>id</code>),
KEY <code>idx_status_live_last</code> (<code>status</code>,<code>live_status</code>,<code>last_live_time</code>),
CONSTRAINT <code>fk_channel_owner</code> FOREIGN KEY (<code>owner_id</code>) REFERENCES <code>user</code> (<code>id</code>) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;</p>

实际跑起来最常被忽略的,是

channel
表的
last_live_time
更新时机——它不能只在主播点击“结束直播”时才写,而要在流媒体服务器检测到推流中断(如 SRS 的 on_publish_done 回调)后,由消息队列触发异步更新。否则观众看到的“最近直播”列表会滞后几分钟。

相关推荐