用户表应仅存身份认证和基础状态字段:id(bigint unsigned)、account_type、account_id、password_hash、status(tinyint)、created_at、updated_at;禁用头像、昵称等社交字段,避免后续分库分表时主键查询性能下降。

用户表要存什么字段,别一上来就加头像和昵称
在线直播系统里,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 回调)后,由消息队列触发异步更新。否则观众看到的“最近直播”列表会滞后几分钟。










