查找聚合性能差

use*_*181 12 mongodb aggregation-framework

我有两个系列

帖子:

{
    "_Id": "1",
    "_PostTypeId": "1",
    "_AcceptedAnswerId": "192",
    "_CreationDate": "2012-02-08T20:02:48.790",
    "_Score": "10",
    ...
    "_OwnerUserId": "6",
    ...
},
...
Run Code Online (Sandbox Code Playgroud)

和用户:

{
    "_Id": "1",
    "_Reputation": "101",
    "_CreationDate": "2012-02-08T19:45:13.447",
    "_DisplayName": "Geoff Dalgas",
    ...
    "_AccountId": "2"
},
...
Run Code Online (Sandbox Code Playgroud)

我想找到写5到15个帖子的用户.这是我的查询的样子:

db.posts.aggregate([
    {
        $lookup: {
            from: "users", 
            localField: "_OwnerUserId",
            foreignField: "_AccountId", 
            as: "X"
        }
    },  
    {
        $group: {
            _id: "$X._AccountId", 
            posts: { $sum: 1 }
        }
    },   
    {
        $match : {posts: {$gte: 5, $lte: 15}}
    },  
    {
        $sort: {posts: -1 }
    },
    {
        $project : {posts: 1}
    }
])
Run Code Online (Sandbox Code Playgroud)

它的工作速度很慢.对于6k用户和10k帖子,在关系数据库中获得响应需要40秒以上,我会在一瞬间获得响应.哪里出了问题?我刚刚开始使用mongodb,我很可能搞砸了这个查询.

bau*_*ace 16

来自https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

foreignField指定from集合中文档的字段.$ lookup在输入文档中对foreignField执行与localField的相等匹配.如果from集合中的文档不包含foreignField,则$ lookup将该值视为null以进行匹配.

这将与任何其他查询执行相同.

如果您在字段_AccountId上没有索引,它将为10,000个帖子中的每一个执行完整的表扫描查询.大部分时间将花在该表扫描中.

db.users.ensureIndex("_AccountId", 1) 
Run Code Online (Sandbox Code Playgroud)

加快进程,因此它执行10,000次索引命中而不是10,000次表扫描.


D G*_*D G 8

除了bauman.space建议在_accountId字段上放置一个索引(这是关键的)之外,你还应该尽早在聚合管道中进行$ match阶段(即作为第一阶段).即使它不使用任何索引(除非您索引posts字段),它将在执行$ lookup(join)阶段之前过滤结果集.

您的查询非常慢的原因是,对于每个帖子,它都为每个用户执行非索引查找(顺序读取).那是大约60米的读数!

查看MongoDB聚合文档管道优化部分.

  • 看待它的方法是,您正在为每个不必要的帖子进行用户查找.您真的只想查找每个用户一次.所以我会按以下顺序执行:$ group(通过_OwnerUserId),$ match,$ lookup,然后$ sort/$ project.它应该大大减少对用户表的读取次数. (3认同)

the*_*mul 5

先用$match然后$lookup$match过滤需要检查的行$lookup。这是有效的。