豆瓣首页的动态流如何设计与实现? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
vJianZhen
V2EX    Python

豆瓣首页的动态流如何设计与实现?

  •  1
     
  •   vJianZhen 2016-04-23 10:05:01 +08:00 6292 次点击
    这是一个创建于 3465 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先看图,有个直观认识:

    豆瓣首页 1 豆瓣首页 2

    豆瓣首页上面的动态的类型有很多,有广播、日记、东西、(想看的)电影、(看过的)电影、友邻新关注的用户……等等很多很多,它是如何设计与实现的?

    疑惑点主要是:

    不同的内容数据存在不同的数据库,比方说广播存在 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 表插入数据时,如何做选择策略。

    可能说的很乱,上一段可以不看。是不是我想复杂了,针对这个问题,不知道各位有什么设计和实现方法?还请不吝赐教,在下万分感激~

    22 条回复    2016-04-24 00:04:11 +08:00
    JiShuTui
        1
    JiShuTui  
       2016-04-23 10:13:22 +08:00
    另有一张 feed 表用于存储,只存其他表的 id 就可以
    vJianZhen
        2
    vJianZhen  
    OP
       2016-04-23 10:23:40 +08:00
    如果一个用户一张 feed 表,那么在关注某友邻以后,该友邻之前的动态怎么插入这个表?截止到什么时候的动态应该插入?相对应地,取关也有这个问题。
    @JiShuTui
    JiShuTui
        3
    JiShuTui  
       2016-4-23 10:24:38 +08:00
    @vJianZhen 我没说一个用户一张啊
    vJianZhen
        4
    vJianZhen  
    OP
       2016-04-23 10:26:06 +08:00
    那能具体说说怎么实现吗?全站一个表也很难想象……。
    @JiShuTui
    zonghua
        5
    zonghua  
       2016-04-23 10:32:36 +08:00 via iPhone
    @JiShuTui 继承然后泛化吗
    bingwenshi
        6
    bingwenshi  
       2016-04-23 10:37:26 +08:00
    跟朋友圈的逻辑是一样的,如果你的友邻更新了内容,只需要插入到你的 feed 流上就行了
    vJianZhen
        7
    vJianZhen  
    OP
       2016-04-23 10:44:57 +08:00
    如果 feed 是一张表,还是那个问题,在关注用户 /取消关注后该用户的动态都要在 feed 表里面插入 /删除吗?

    再有,这个表中的记录有多少,如果你有 150 个好友,平均一个好友有 500 条动态,难道这个表就有 150*500 个记录吗?

    feed 表是否是动态的呢?因为如果用户没有刷得很深(不去查看很久以前的动态),那些动态是没有必要显示的,那那些动态还在表里面吗?还是这个表是随着用户上拉查看而动态生成的?
    @bingwenshi
    bingwenshi
        8
    bingwenshi  
       2016-04-23 11:00:26 +08:00   1
    虽然我没具体去了解过这部分,不过有几点可以讨论下

    1. 关注用户,应该不会再去插入到 feed 里面,但是这个友邻新生成消息可以插入到你的 feed 上
    2. 取关,直接在你的 feed 里面删了这个友邻的信息就可以了
    bingwenshi
        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
    vJianZhen
        10
    vJianZhen  
    OP
      &nbp;2016-04-23 11:13:00 +08:00
    这个动态流是不是有这样的特性:我新关注的用户在这个关注时间点所发布的所有动态不会出现在 feed 表中,即如果我在今天关注了 xxx ,那我往后翻我的首页动态流,不会出现 xxx 在昨天前天以前发布的动态?

    会有这个想法是因为按照你所说的实现方案,那新关注的用户的旧的动态流就没有必要加入 feed 表中。

    其实,我大概赞同你的想法。
    @bingwenshi
    vJianZhen
        11
    vJianZhen  
    OP
       2016-04-23 11:15:02 +08:00
    我的也是,最终到 79 页。

    @bingwenshi 这样的话,我慢慢理清思路了。谢谢~
    pubby
        12
    pubby  
       2016-04-23 11:27:05 +08:00
    通常关注的人不会太多(比如 100 ),所以另外一个思路是我的 feed 表只有自己的数据,同时把 feed 中最新 1000 条(比如)放入 redis

    然后一次性获取 100 人的 top1000 ,重排序就行了
    vJianZhen
        13
    vJianZhen  
    OP
       2016-04-23 11:28:26 +08:00
    没用过 redis ,它的作用是缓存么?
    @pubby
    pubby
        14
    pubby  
       2016-04-23 11:47:15 +08:00   1
    redis 只是一种思路,因为常规数据库 IN (100 个 id) 的条件查询效率不高,不过 top1000 放内存表也是另外一种优化思路。

    如果要最严谨的实现,那就是所有你关注的人的动态都要插入你自己的 timeline 列表
    kongkongyzt
        15
    kongkongyzt  
       2016-04-23 12:02:00 +08:00
    @vJianZhen 我试了一下, 在豆瓣上关注了一个在 2 小时前, 2 天前都发表了动态的人, 关注后刷新我的主页, 立马显示了他 2 小时前和 2 天前的那几条动态
    vJianZhen
        16
    vJianZhen  
    OP
       2016-04-23 12:03:48 +08:00
    我也尝试过,跟你的情况一样。也许豆瓣有更好的实现方案吧,这也是我这个问题最想知道的。
    @kongkongyzt
    junnplus
        17
    junnplus  
       2016-04-23 12:26:43 +08:0
    feed 流大体上有两种实现方式,一种推,一种拉
    pubby
        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

    “相册表写好了之后,会触发一个批处理的动作。这个动作就是去跟小王的每个好友说,小王有一个新的发布,请把这个发布插入到每个好友的时间线里面去。”

    “这是一个单数据副本写扩散的过程。但是相对应的,读取就很简单了,每一个用户只需要读取自己的时间线表,就这一个动作就行,而不需要去遍历所有好友的相册表。”
    vincenttone
        20
    vincenttone  
       2016-04-23 16:28:26 +08:00   1
    就看要不要根据目前的关注变化动态调整从前的消息了,如果有的话就需要增加一些操作,但是大体上都可以通过一个核心消息服务来解决。
    各个模块在上线前按照一定规则推送消息到一个队列里, feed 那头接收消息存储就可以了。
    如果需要动态调整从前的内容的话,就需要建一个库处理了,里面都是冗余数据,需要根据其他模块的状态和数据做调整。
    wannianma
        21
    wannianma  
       2016-04-23 18:52:13 +08:00 via iPhone
    更新数据方面,我们的思路是使用触发器
    WIwindson
        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/
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2754 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 09:31 PVG 17:31 LAX 02:31 JFK 05:31
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86