Feed流系统设计实践(一)
关系内容Feed流
关系内容Feed流简单介绍
在当前任何具有社交场景的app应用中,用户之间会因为很多行为产生关系,例如微信好友的关系,当前各种陌生人社交软件喜欢的关系,微博粉丝与博 主的关系,当前各种直播软件中粉丝与主播的关系。当前产生关系之后,一个用户所有关注的用户产生的内容就形成Feed流,这个时候需要设计一个合理的Feed流系统。
现在大多数关系产生Feed流系统可以分为两种实践场景,一种是朋友圈类型,我在这里叫做wx类型,一种是微博关注博主类型,我在这里叫做wb类型。
wx类型场景,一个用户被关注的其他用户的数量上限不会太大,例如微信一个用户的好友多一点可能就几千人,这里可以设计为写扩散模式。
wb类型场景,一个用户被关注的其他用户的数量可能是一个很大的值,例如微博一个大V博主的粉丝可能达到几十万、几百万、几千万这种,这里需要设计为读扩散模式。
什么是写扩散模式,什么是读扩散模式?顾名思义,写扩撒,就是将一个内容写多份,例如一个用户发一条朋友圈内容,除了写入自己的朋友圈列表外,需要将这条朋友圈内容写入所有关注他的其他用户的朋友圈内容列表,这样就是一条朋友圈内容就的发布就要写入多个列表,这个就叫做写扩散;读扩撒,就是一个内容被多读,例如一个拥有千万粉丝的微博大V发布了一条微博,不可能将这条内容逐一写入到所有粉丝关注内容列表里,这样的一个内容写入时间会很长,这样会导致一次瞬间写入量会很大,如果一个时间段有很多大V同时发布微博,写扩散的模式对系统的性能会有很大的损耗,这样就只能先只写一份内容,等到用户下次来刷新查看的时候再来直接获取这个大V用户发布的内容,这样就是读扩散的模式。
但是,读扩撒的设计也会有一个问题,那就是如果一个用户关注了几万,几十万个大V博 主怎么办呢?难到是一次将所有关注的大V博发布的内容全部拿出来?显然这样肯定是不合理的,下面两种模式下的Feed流系统设计中会有解答。
关系内容Feed流系统设计
1. wx类型关系类容Feed流系统
Feed系统架构图
Feed写入和查询时序图
Feed写入流程图
Feed查询流程图
mysql数据库设计
feed_info表结构设计
字段 | 类型 | 说明 |
---|---|---|
id | bigint | 主键id, incr id |
uid | bigint | feed内容所属用户id |
create_uid | bigint | feed内容创建用户id |
feed_id | bigint | feed内容id |
type | int | feed内容类型 |
content | varbinanry(4096) | feed内容信息 |
status | tinyint | feed内容状态, 默认status=1, status=1正常, status=2删除 |
score_id | bigint | feed内容排序id |
create_time | datetime | feed内容创建时间 |
update_time | datetime | feed内容更新时间 |
索引设计
uid、score_id普通索引 create_uid、feed_id唯一索引
分库分表策略
根据uid来分表
feed内容设计
feed的内容使用pb压缩字符串, 定义feed类型字段用于标记不同类型的feed, 方便拓展
feed_id与score_id生成规则
feed_id生成规则根据创建者用户id生成, 非全局唯一,但是对于每一个创建feed用户唯一 score_id按照创建时间按照时间序生成,为16位的int,用于做feed的排序
redis缓存设计
Feed list缓存设计
使用zset结构 key=uid val=create_uid:feed_id score=score_id list 列表只维护固定长度,淘汰老数据
Feed info缓存设计
使用hash结构 key=uid field=create_uid:feed_id val={"content": content, "type": type, "create_time": create_time, ...}
2. wb类型关系类容Feed流系统
wb类型采用读扩散模式,用户关注的博主或主播在关注Feed列表内当用户刷新时只是展示用户最新的一条Feed内容,不展示全量Feed内容。此外会将已经展示给用户的Feed内写入用户历史Feed列表中,这就是读扩散的模式。
Feed系统架构图
Feed写入和查询时序图
Feed写入流程图
Feed查询流程图
mysql数据库设计
feed_info表结构设计与写扩散模式一致
redis缓存设计
feed list缓存与feed info缓存与写扩散模式一致
读取关注用户列表offset缓存
使用kv结构 key=uid val=offset
用户最新的Feed缓存
使用kv结构 key=uid val={"content": content, "type": type, "create_time": create_time, "score_id": score_id, "feed_id": feed_id, ...}
其他
feed的状态更新,例如删除操作,通过消费kafka队列来进行同步数据,更新缓存与数据库。
关于每条Feed的评论、点赞、转发不在本文所讨论范围,这些只是feed的某一属性,不属于feed内容本身。
结尾
这个是两种模式下关注Feed的设计,有更好的设计欢迎提出建议,作者一定会积极采纳。