现在有一个 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:'...'}
_id
或发帖时间倒序排列。follows = db.follow.find_one({uid:111}).follows
db.post.aggregate([
{$match: {uid: {$in:follows}}},
{$sort: {_id:-1}},
{$limit: 10}
])
大致是这么个思路。
1
fakeshadow 2019-02-26 00:37:40 +08:00 1
复杂的查询不是很懂。
用$lookup 把 follow 的 uid 拿过来再 match 不知道可以不。 用$gt 来查询应该就可以实现翻页吧。 |
2
JCZ2MkKb5S8ZX9pq OP @fakeshadow 我也想拿到 aggregate 里面来,但不知道用哪个命令。
gt 只有在 id 升序或降序才可以吧。可现在 id 是 16 位英数混排的,很可能不行。 |
3
fortunezhang 2019-02-26 08:56:19 +08:00
不建议你这么做,流量稍微一大就压死数据库。 推荐你去看下 QQ 空间的算法。
|
4
brickyang 2019-02-26 09:37:03 +08:00
1. ObjectID 是有序的,可以用来翻页,自定义乱序 id 基本没办法了
2. https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/ |
5
luw2007 2019-02-26 09:37:57 +08:00
homeline timeline
很少会用数据库直接生成 大部分都是在发|删帖的时候用逻辑改变 homeline 和 timeline |
6
whusnoopy 2019-02-26 10:17:41 +08:00 1
以前在人人做过,这种 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 条的差别 |
7
JCZ2MkKb5S8ZX9pq OP @luw2007 那如果某个用户有很多 follower,他每发一帖,都要去更新所有 follower 的 timeline 吗?
另外讨论下: follower 的 timeline 条数有上限嘛?还是超了那个上限再去数据库里抓? 如果有非活跃 follower,会不会做太多额外的开销? |
8
JCZ2MkKb5S8ZX9pq OP @whusnoopy 请教一下
现在尝试下来,直接 aggregate 查询,好像是返回 cursor,是个 generator。 这时候加上 limit,速度其实是挺快的。去掉 limit,数据量大就很慢了。 有没有什么办法可以利用这个 cursor 做翻页? 感觉可能会比重搜一次 post_id 会快一点 |
9
JCZ2MkKb5S8ZX9pq OP |
10
luw2007 2019-02-26 12:26:38 +08:00
@JCZ2MkKb5S8ZX9pq 用户量少就不要考虑拆分活跃用户和非活跃用户。只会增加代码的复杂度。
完备的做法就是再造一个微博。 owner 发布之后,直接更新 follower 的 timeline |
11
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 默认都是有限的,翻前一两页和翻很多页的逻辑都是要区分,前面是缓存好的数据,后面是实时计算。如果你的用户量真的大到这种规模,自然会有人来帮你架构优化的(比如微博) |
12
JCZ2MkKb5S8ZX9pq OP @whusnoopy 嗯,明白。
再请教个问题,假设先用纯拉取的方式。 我现在是分两步,先从一个记录关系的 collection 获取关注列表,然后到 post 的 collection 获取指定关注列表发的帖子,mongo 有没有什么命令能直接写到一个 aggregate 里嘛?还是一定要分两步来? |
13
whusnoopy 2019-02-26 22:28:31 +08:00 via Android
@JCZ2MkKb5S8ZX9pq 这个不清楚,我都自己裸写的……
|
14
JCZ2MkKb5S8ZX9pq OP @whusnoopy 搞定了,直接写里面就完了,直接把查询写在$in 后面,不需要赋值再传递。
实测查询的话,的确用单增 id 比较 lt 快一点。 skip 的话,skip100 大概 0.1s ,skip10000 大概 0.2s ,估计量大了还是差蛮多的。 而且再次查询的话 skip 位置可能有偏移。 |