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 · 5888 次点击
    这是一个创建于 3134 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先看图,有个直观认识:

    豆瓣首页 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-04-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
       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:00
    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   ·   实用小工具   ·   993 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 20:10 · PVG 04:10 · LAX 12:10 · JFK 15:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.