Mongodb 小白问题,首页显示关注的人的帖子,一般怎么实现?

2019-02-26 00:11:36 +08:00
 JCZ2MkKb5S8ZX9pq

现在有一个 db.follow

{uid:111, follows:[001, 002, 003]}
{uid:222, follows:[005, 006, 007, 008]}
...

然后有一个 db.post

{id:00001, uid:001, text:'...'}
{id:00002, uid:001, text:'...'}
{id:00003, uid:005, text:'...'}

目的


方法

follows = db.follow.find_one({uid:111}).follows
db.post.aggregate([ 
    {$match: {uid: {$in:follows}}}, 
    {$sort: {_id:-1}},
    {$limit: 10}
])

大致是这么个思路。


请问

8116 次点击
所在节点    MongoDB
14 条回复
fakeshadow
2019-02-26 00:37:40 +08:00
复杂的查询不是很懂。
用$lookup 把 follow 的 uid 拿过来再 match 不知道可以不。
用$gt 来查询应该就可以实现翻页吧。
JCZ2MkKb5S8ZX9pq
2019-02-26 00:59:08 +08:00
@fakeshadow 我也想拿到 aggregate 里面来,但不知道用哪个命令。
gt 只有在 id 升序或降序才可以吧。可现在 id 是 16 位英数混排的,很可能不行。
fortunezhang
2019-02-26 08:56:19 +08:00
不建议你这么做,流量稍微一大就压死数据库。 推荐你去看下 QQ 空间的算法。
brickyang
2019-02-26 09:37:03 +08:00
1. ObjectID 是有序的,可以用来翻页,自定义乱序 id 基本没办法了
2. https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
luw2007
2019-02-26 09:37:57 +08:00
homeline timeline
很少会用数据库直接生成
大部分都是在发|删帖的时候用逻辑改变 homeline 和 timeline
whusnoopy
2019-02-26 10:17:41 +08:00
以前在人人做过,这种 SNS 的首页或信息流,都是有额外的缓存的,当年写过一篇分析,见 https://www.yewen.us/blog/2013/03/push-or-pull-on-sns/

关于翻页用 post_id,最大的好处是可以利用主 key 来做查询加速,查询语句里写 {post_id: {$gt: post_id}}, limit=10 就好,如果是 {}, limit=10, skip=20 的写法,DB 还是要先把前面 20 条取出来扫过去再拿后面的,翻页到后面时性能开销极大,特别是如果还加了其他的条件,可能就是从 100 条记录里选 10 条,和从 10000 条记录里选 10 条的差别
JCZ2MkKb5S8ZX9pq
2019-02-26 11:42:46 +08:00
@luw2007 那如果某个用户有很多 follower,他每发一帖,都要去更新所有 follower 的 timeline 吗?
另外讨论下:
follower 的 timeline 条数有上限嘛?还是超了那个上限再去数据库里抓?
如果有非活跃 follower,会不会做太多额外的开销?
JCZ2MkKb5S8ZX9pq
2019-02-26 11:47:32 +08:00
@whusnoopy 请教一下
现在尝试下来,直接 aggregate 查询,好像是返回 cursor,是个 generator。
这时候加上 limit,速度其实是挺快的。去掉 limit,数据量大就很慢了。
有没有什么办法可以利用这个 cursor 做翻页?
感觉可能会比重搜一次 post_id 会快一点
JCZ2MkKb5S8ZX9pq
2019-02-26 12:11:11 +08:00
@whusnoopy

> 新浪那个前端加载速度, 绝对不比后台做一次拉操作快, 所以用户几乎感觉不到……

这个吐槽,笑死。
luw2007
2019-02-26 12:26:38 +08:00
@JCZ2MkKb5S8ZX9pq 用户量少就不要考虑拆分活跃用户和非活跃用户。只会增加代码的复杂度。
完备的做法就是再造一个微博。

owner 发布之后,直接更新 follower 的 timeline
whusnoopy
2019-02-26 15:19:14 +08:00
@JCZ2MkKb5S8ZX9pq 这个取决于你的最终实现

就这个问题来说,我们先关注 aggregate 到底做了什么。裸的实现应该是从每个 followee 那拿 sort 好的 limit 条数据,然后合并到一起,再 sort 取 limit。如果你需要自己优化这个事情,那就是先从每个 followee 那拿从 post_id 往前的 limit 条数据,然后把所有 followee 的 limit 条数据合并成 n*limit 条,再排序取 limit (这里有个假设是 post_id 是全局单增的)

回到 cursor 的问题,这个 cursor 很可能只有 limit 条,再往后 MongoDB 压根就没给你准备数据,或者还是要按前面我说的那样去重复计算

我觉得你先不用考虑活跃用户和非活跃用户,每个人的 timeline 默认都是有限的,翻前一两页和翻很多页的逻辑都是要区分,前面是缓存好的数据,后面是实时计算。如果你的用户量真的大到这种规模,自然会有人来帮你架构优化的(比如微博)
JCZ2MkKb5S8ZX9pq
2019-02-26 16:48:15 +08:00
@whusnoopy 嗯,明白。
再请教个问题,假设先用纯拉取的方式。
我现在是分两步,先从一个记录关系的 collection 获取关注列表,然后到 post 的 collection 获取指定关注列表发的帖子,mongo 有没有什么命令能直接写到一个 aggregate 里嘛?还是一定要分两步来?
whusnoopy
2019-02-26 22:28:31 +08:00
@JCZ2MkKb5S8ZX9pq 这个不清楚,我都自己裸写的……
JCZ2MkKb5S8ZX9pq
2019-02-26 23:14:40 +08:00
@whusnoopy 搞定了,直接写里面就完了,直接把查询写在$in 后面,不需要赋值再传递。
实测查询的话,的确用单增 id 比较 lt 快一点。
skip 的话,skip100 大概 0.1s ,skip10000 大概 0.2s ,估计量大了还是差蛮多的。
而且再次查询的话 skip 位置可能有偏移。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/538693

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX