《互联网产品的服务端架构·内容、社交与实时互动》附录 A 内容与社交数据模型

把卷二涉及的内容、媒体、审核、关系、评论、计数、会话、消息、已读与索引整理成一张可对照的数据模型:每张表列出关键字段、类型、索引与写入方,并说明哪些字段可以冗余、哪些必须由单一服务负责写入,附冗余字段判定表、迁移顺序建议与常见建模错误对照。

本附录把卷二各章出现的表整理在一起,方便读者对照代码与设计自己的库表。字段命名与正文一致,类型以常见关系型数据库为参照;实际项目里字符串长度、时间精度、分区策略需要按规模调整。

先说明三条通用约定:

  • 所有表都有 created_at 与 updated_at,下文不再重复列出。
  • 主键统一用 id(字符串,应用侧生成),需要自增序号时单独加 seq 字段。
  • 软删除统一用 deleted_at(可空时间),不用布尔字段,避免「删除时间」丢失。

内容主表

内容主表只存「内容是什么」与「内容处于什么状态」,不存审核详情、不存统计数、不存正文大字段。

字段类型说明写入方
idvarchar(32)内容 ID,应用生成内容服务
author_idvarchar(32)作者用户 ID内容服务
kindvarchar(16)text / image / video / mixed内容服务
titlevarchar(200)标题,可空内容服务
bodytext短文本正文,长正文另存内容服务
statevarchar(16)draft / reviewing / published / rejected / deleted内容服务
visibilityvarchar(16)public / friends / private内容服务
published_atbigint发布时间,未发布为空内容服务
deleted_atbigint软删除时间内容服务
versionbigint乐观锁版本号内容服务

索引建议:

  • (author_id, state, published_at desc):作者主页与「我的内容」。
  • (state, published_at desc):运营后台与审核队列。
  • (id, version):更新时校验版本,避免并发覆盖。

正文与状态放在同一张表,是因为它们总是一起被读取;但大字段(长文、富文本)建议拆到 content_body,避免列表查询把大字段带进缓冲池。

媒体表

一条内容可以挂多个媒体,媒体表与内容表是一对多。

字段类型说明
idvarchar(32)媒体 ID
content_idvarchar(32)所属内容
media_kindvarchar(16)image / video / audio
object_keyvarchar(255)对象存储键
width / heightint图片或视频尺寸
duration_msint视频时长,图片为 0
size_bytesbigint文件大小
statusvarchar(16)uploading / transcode / ready / failed
sort_orderint在内容中的顺序

索引:(content_id, sort_order)。对象存储的键不建议直接暴露给客户端,读接口返回签名 URL 或 CDN 地址。

status 与内容状态是两条独立的状态机:内容可以先发布,媒体后转码完成。读接口要能处理「内容已发布、媒体还没 ready」的情况,通常返回占位图而不是报错。

内容状态迁移

状态迁移用一张表约束,而不是散落在各个接口的 if 分支里。

从到触发者条件
draftreviewing作者提交元数据校验通过
reviewingpublished审核服务机审通过或人审通过
reviewingrejected审核服务机审拒绝或人审拒绝
reviewingdraft作者撤回尚未产生审核结论
publisheddeleted作者或管理员有权限
rejecteddraft作者修改在申诉时限内
deletedpublished管理员恢复有恢复权限,且未超过保留期

这张表是代码里 Allowed(Transition) 的权威来源。新增状态时先改表,再改代码;反过来做,迟早会出现「代码允许、产品不认」的迁移。

几个容易漏的约束:

  • rejected 不能直接到 published,必须经过作者修改再重新提交。
  • deleted 到 published 只允许管理员操作,且要有审计记录。
  • reviewing 到 draft 只允许在审核结论产生前,否则会出现「审核通过但内容被撤回」的竞态。

审核记录表

审核记录与内容是一对多:一次内容可能经历多轮机审、人审、复审。

字段类型说明
idvarchar(32)审核记录 ID
content_idvarchar(32)被审核内容
roundint第几轮审核
stagevarchar(16)machine / human / appeal
decisionvarchar(16)approve / reject / escalate
confidencedecimal(5,4)机审置信度,人审为空
reason_codevarchar(32)拒绝原因码
reviewer_idvarchar(32)人审员,机审为空
decided_atbigint结论时间

索引:(content_id, round)、(reviewer_id, decided_at)。

审核记录只追加、不更新。需要修正结论时新增一条记录,而不是改旧记录;否则无法回答「当时为什么这么判」。

关系表

社交关系用一张表存三种关系,通过 relation 区分,而不是三张表。

字段类型说明
from_idvarchar(32)关系发起方
to_idvarchar(32)关系目标方
relationvarchar(16)follow / friend / block / mute
statevarchar(16)active / pending / removed
created_atbigint建立时间

主键:(from_id, to_id, relation)。索引:(to_id, relation, state) 用于查粉丝与好友。

几点设计说明:

  • 关注是单向记录,好友是两条记录(A→B 与 B→A 都是 friend),拉黑是一条记录且优先级最高。
  • mute 与 block 不同:mute 只影响推荐与通知,不影响可见性;block 影响可见性与互动。
  • 关系解除用 state=removed,保留历史;但查询时必须过滤,否则会出现「删了好友还能看到」。

关系判定链的顺序必须是 block → 内容可见性 → 好友/关注 → 场景规则。顺序换一下,就会出现「拉黑的人通过关注关系绕过」。

评论表

评论是树,但存储用邻接表加物化路径,避免递归查询。

字段类型说明
idvarchar(32)评论 ID
content_idvarchar(32)所属内容
author_idvarchar(32)评论者
parent_idvarchar(32)父评论,顶级为空
root_idvarchar(32)所属根评论
depthint层级,顶级为 1
bodytext评论正文
statevarchar(16)visible / folded / deleted
like_countint冗余计数

索引:(content_id, root_id, created_at)、(parent_id, created_at)。

depth 在写入时计算并固化,读取时不再递归。超过 maxDepth 的回复挂到父级下并标记「展开更多」,而不是拒绝写入。

like_count 是冗余字段,允许短暂不一致;真相在互动关系表,计数由异步任务对账。

互动关系表

点赞、收藏、关注内容等「用户对目标的态度」统一存成关系表。

字段类型说明
user_idvarchar(32)操作者
target_typevarchar(16)content / comment / user
target_idvarchar(32)目标 ID
actionvarchar(16)like / favorite / hide
created_atbigint操作时间

主键:(user_id, target_type, target_id, action)。索引:(target_type, target_id, action) 用于统计与「谁赞过」。

这张表是计数的真相来源。缓存里的计数、内容表上的冗余计数都是派生值,对账任务以这张表为准。

计数表

计数表按「对象 + 指标」存物化结果,可选按天分桶。

字段类型说明
target_typevarchar(16)content / comment / user
target_idvarchar(32)目标 ID
metricvarchar(16)like / comment / view / share
valuebigint当前值
versionbigint对账版本

主键:(target_type, target_id, metric)。

高频写入的计数(如浏览量)建议先写 Redis 或本地聚合,再批量落库;低频但要求准确的计数(如收藏数)可以同步更新。两类计数不要放在同一张表同一套更新路径里,否则要么慢,要么不准。

会话与消息表

IM 的存储按会话分片,消息表以 conversation_id 为分区键。

会话表:

字段类型说明
idvarchar(64)会话 ID
typevarchar(16)single / group / system
member_hashvarchar(64)单聊去重键
max_membersint群成员上限
last_seqbigint会话最新序号
last_message_atbigint最后一条消息时间

消息表:

字段类型说明
conversation_idvarchar(64)会话 ID
seqbigint会话内单调序号
msg_idvarchar(64)客户端或服务端生成的消息 ID
sender_idvarchar(32)发送者
kindvarchar(16)text / image / system
bodytext消息内容
sent_atbigint发送时间
recalled_atbigint撤回时间,可空

主键:(conversation_id, seq);唯一索引:(conversation_id, msg_id) 用于去重。

seq 由会话服务在写入时分配,保证会话内单调;跨会话不要求有序。客户端重发时带着原 msg_id,服务端命中唯一索引后返回原 seq,而不是插入新消息。

已读表

已读状态按「用户 + 会话 + 设备」存,读取时取最大值。

字段类型说明
user_idvarchar(32)用户
conversation_idvarchar(64)会话
device_idvarchar(64)设备
read_seqbigint已读到的最大序号
updated_atbigint更新时间

主键:(user_id, conversation_id, device_id)。

未读数 = last_seq - max(read_seq),下限为 0。多端已读取最大值,而不是求和;求和会出现「手机读了、电脑没读,未读变成负数或翻倍」。

已读表更新非常频繁,建议只保留最近活跃设备,历史设备定期清理。

搜索索引表

搜索索引是独立的读模型,字段与主表不同。

字段类型说明
doc_idvarchar(32)内容 ID
titlevarchar(200)标题
bodytext可搜索正文
author_idvarchar(32)作者
statevarchar(16)内容状态快照
visibilityvarchar(16)可见性快照
versionbigint内容版本,用于幂等
indexed_atbigint索引时间
deletedboolean墓碑标记

索引同步用版本号做幂等:只有 version 更大的变更才应用。删除时写墓碑,而不是物理删除,避免旧消息晚到后把文档「复活」。

搜索结果的可见性过滤必须在查询阶段完成,不能只依赖索引里的快照字段;因为用户关系可能已经变化,而索引还没更新。

哪些字段可以冗余

冗余字段能减少一次查询,但会引入一致性问题。下面这张表给出卷二涉及字段的默认建议。

字段是否冗余一致性要求对账方式
内容 like_count是最终一致定时按关系表重算
评论 like_count是最终一致同上
会话 last_seq是强一致(同会话内)写入时同事务更新
会话 last_message_at是最终一致消息写入后更新
搜索 state是最终一致版本号幂等
Feed 收件箱条目是最终一致以内容表为准
未读数否(派生)读时计算不需要对账

判断标准很简单:如果这个字段错了,用户会不会立刻发现?会,就不能只靠异步;不会,就可以冗余加对账。

常见建模错误

最后列几个在评审中反复出现的建模错误,读者可以拿来对照自己的库表。

  • 用布尔字段表达多状态。 is_published、is_deleted、is_reviewing 三个布尔可以组合出八种状态,其中五种是非法的。用枚举加迁移表。
  • 把关系存成一张无类型的边表。 (from, to) 不区分关注、好友、拉黑,查询时靠业务代码猜。加 relation 字段。
  • 把已读存成未读数。 未读数是派生值,多端同步时无法合并。存已读序号。
  • 删除时物理删除。 索引、缓存、Feed 都还在引用,物理删除会让它们返回 404 或旧数据。用软删除加墓碑。
  • 把搜索索引当主库查。 索引有滞后,且字段不全;详情页必须回主库。
  • 消息表用全局自增 ID 当序号。 会话内顺序会跳号,客户端无法判断「有没有漏消息」。用会话内序号。

这些错误的共同点是:它们在低并发、单端、短链路的测试里都不会暴露。只有在多端、重试、删除、审核同时发生时才会出现。设计时多问一句「这个字段在并发和重试下还成立吗」,能省下很多线上排查。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《互联网产品的 AI 架构·平台化与治理》附录 C 平台化评审清单
  2. 《互联网产品的 AI 架构·平台化与治理》附录 B 能力目录与配额字段速查
  3. 《互联网产品的 AI 架构·平台化与治理》附录 A 治理角色与职责速查