先看图,有个直观认识:
豆瓣首页上面的动态的类型有很多,有广播、日记、东西、(想看的)电影、(看过的)电影、友邻新关注的用户……等等很多很多,它是如何设计与实现的?
疑惑点主要是:
不同的内容数据存在不同的数据库,比方说广播存在 boardcast 表,日记存在 note 表,电影存在 movie 表……而豆瓣首页是按时间呈现动态的,如何在各个表中取出数据再做时间倒序排?难道是联合查询吗,那样的话用在查询数据库上面的时间岂不是很大。
再有就是如何针对不同的内容数据来渲染,比方说广播需要显示内容和赞评论等,而新写的日记显示摘要外还显示『喜欢』按钮……不同的内容虽然有大致一样的样式,但是细节上面又有区别,那么怎么显示?如果在 HTML 模板上使用模板引擎(如 Jinja2 )的 for 和 if 语法,但不同的内容类型不能写在一个 for 里面( for i in content ),因为模型对象的类型不一致;即使能克服上面的问题,那在 if 语句里面怎么判断内容的类型,再针对性地写渲染样式?
我能想到的是再加一个『中间表 timeline 』。当用户发表了日记,除了把数据写入 note 表,再把内容摘要写入 timeline 表(如内容类型代码 xxx ,时间 xxx ,内容 IDxxx 等);当用户发表了广播,除了把数据写入 boardcast 外,再把内容摘要写入 timeline 表(如内容类型 xxx ,时间 xxx ,内容 IDxxx 等)……这样首页动态流只需要从 timeline 表统一取数据,再根据不同的内容类型及其 ID 写不同的渲染样式。可是又有一个问题,如果我取关友邻, ta 的动态就不该出现在我的首页上,即在往这个 timeline 表插入数据时,如何做选择策略。
可能说的很乱,上一段可以不看。是不是我想复杂了,针对这个问题,不知道各位有什么设计和实现方法?还请不吝赐教,在下万分感激~
1
JiShuTui 2016-04-23 10:13:22 +08:00
另有一张 feed 表用于存储,只存其他表的 id 就可以
|
2
vJianZhen OP 如果一个用户一张 feed 表,那么在关注某友邻以后,该友邻之前的动态怎么插入这个表?截止到什么时候的动态应该插入?相对应地,取关也有这个问题。
@JiShuTui |
6
bingwenshi 2016-04-23 10:37:26 +08:00
跟朋友圈的逻辑是一样的,如果你的友邻更新了内容,只需要插入到你的 feed 流上就行了
|
7
vJianZhen OP 如果 feed 是一张表,还是那个问题,在关注用户 /取消关注后该用户的动态都要在 feed 表里面插入 /删除吗?
再有,这个表中的记录有多少,如果你有 150 个好友,平均一个好友有 500 条动态,难道这个表就有 150*500 个记录吗? feed 表是否是动态的呢?因为如果用户没有刷得很深(不去查看很久以前的动态),那些动态是没有必要显示的,那那些动态还在表里面吗?还是这个表是随着用户上拉查看而动态生成的? @bingwenshi |
8
bingwenshi 2016-04-23 11:00:26 +08:00 1
虽然我没具体去了解过这部分,不过有几点可以讨论下
1. 关注用户,应该不会再去插入到 feed 里面,但是这个友邻新生成消息可以插入到你的 feed 上 2. 取关,直接在你的 feed 里面删了这个友邻的信息就可以了 |
9
bingwenshi 2016-04-23 11:04:57 +08:00
feed 这个数据应该不会保存你所有的数据,另外,数据不是动态生成的,是一开始就插入好了,包括 title , summary
你可以测试下嘛,我这边是到第 79 页 https://www.douban.com/?p=79 第 80 页就没数据了 https://www.douban.com/?p=80 |
10
vJianZhen OP 这个动态流是不是有这样的特性:我新关注的用户在这个关注时间点所发布的所有动态不会出现在 feed 表中,即如果我在今天关注了 xxx ,那我往后翻我的首页动态流,不会出现 xxx 在昨天前天以前发布的动态?
会有这个想法是因为按照你所说的实现方案,那新关注的用户的旧的动态流就没有必要加入 feed 表中。 其实,我大概赞同你的想法。 @bingwenshi |
11
vJianZhen OP |
12
pubby 2016-04-23 11:27:05 +08:00
通常关注的人不会太多(比如 100 ),所以另外一个思路是我的 feed 表只有自己的数据,同时把 feed 中最新 1000 条(比如)放入 redis
然后一次性获取 100 人的 top1000 ,重排序就行了 |
14
pubby 2016-04-23 11:47:15 +08:00 1
redis 只是一种思路,因为常规数据库 IN (100 个 id) 的条件查询效率不高,不过 top1000 放内存表也是另外一种优化思路。
如果要最严谨的实现,那就是所有你关注的人的动态都要插入你自己的 timeline 列表 |
15
kongkongyzt 2016-04-23 12:02:00 +08:00
@vJianZhen 我试了一下, 在豆瓣上关注了一个在 2 小时前, 2 天前都发表了动态的人, 关注后刷新我的主页, 立马显示了他 2 小时前和 2 天前的那几条动态
|
16
vJianZhen OP 我也尝试过,跟你的情况一样。也许豆瓣有更好的实现方案吧,这也是我这个问题最想知道的。
@kongkongyzt |
17
junnplus 2016-04-23 12:26:43 +08:00
feed 流大体上有两种实现方式,一种推,一种拉
|
19
pubby 2016-04-23 12:35:30 +08:00 1
@vJianZhen 可以看一下这个 http://www.infoq.com/cn/articles/three-people-background-team-and-billions-daily-release
“相册表写好了之后,会触发一个批处理的动作。这个动作就是去跟小王的每个好友说,小王有一个新的发布,请把这个发布插入到每个好友的时间线里面去。” “这是一个单数据副本写扩散的过程。但是相对应的,读取就很简单了,每一个用户只需要读取自己的时间线表,就这一个动作就行,而不需要去遍历所有好友的相册表。” |
20
vincenttone 2016-04-23 16:28:26 +08:00 1
就看要不要根据目前的关注变化动态调整从前的消息了,如果有的话就需要增加一些操作,但是大体上都可以通过一个核心消息服务来解决。
各个模块在上线前按照一定规则推送消息到一个队列里, feed 那头接收消息存储就可以了。 如果需要动态调整从前的内容的话,就需要建一个库处理了,里面都是冗余数据,需要根据其他模块的状态和数据做调整。 |
21
wannianma 2016-04-23 18:52:13 +08:00 via iPhone
更新数据方面,我们的思路是使用触发器
|
22
WIwindson 2016-04-24 00:04:11 +08:00
如果不局限于关系型数据库的话,可以参考 redis 官方教程 http://www.redis.cn/topics/twitter-clone.html 。另外可以参考下这篇文章 http://timyang.net/architecture/pinterest-feed/
|