【问题标题】:MongoDB Aggregation is slow on Indexed fieldsMongoDB 聚合在索引字段上很慢
【发布时间】:2018-11-25 06:08:27
【问题描述】:

我有一个包含约 250 万个文档的集合,集合大小为 14,1GB,存储大小为 4.2GB,平均对象大小为 5,8KB。我在dataSourceNameversion(文本字段)中的两个字段上创建了两个单独的索引,并尝试进行聚合查询以列出它们的“分组依据”值。 (试图实现这一点:select dsn, v from collection group by dsn, v)。

db.getCollection("the-collection").aggregate(
    [
        { 
            "$group" : {
                "_id" : {
                    "dataSourceName" : "$dataSourceName", 
                    "version" : "$version"
                }
            }
        }
    ], 
    { 
        "allowDiskUse" : false
    }
);

尽管 MongoDB 在服务器上占用了大约 10GB 的 RAM,但字段已编入索引并且根本没有其他任何东西在运行,聚合需要大约 40 秒。

我尝试创建一个新的索引,它包含两个字段的顺序,但是,查询似乎没有使用索引:

{ 
    "stages" : [
        {
            "$cursor" : {
                "query" : {

                }, 
                "fields" : {
                    "dataSourceName" : NumberInt(1), 
                    "version" : NumberInt(1), 
                    "_id" : NumberInt(0)
                }, 
                "queryPlanner" : {
                    "plannerVersion" : NumberInt(1), 
                    "namespace" : "db.the-collection", 
                    "indexFilterSet" : false, 
                    "parsedQuery" : {

                    }, 
                    "winningPlan" : {
                        "stage" : "COLLSCAN", 
                        "direction" : "forward"
                    }, 
                    "rejectedPlans" : [

                    ]
                }
            }
        }, 
        {
            "$group" : {
                "_id" : {
                    "dataSourceName" : "$dataSourceName", 
                    "version" : "$version"
                }
            }
        }
    ], 
    "ok" : 1.0
}

我在 Windows 上使用 MongoDB 3.6.5 64bit,所以它应该使用索引:https://docs.mongodb.com/master/core/aggregation-pipeline/#pipeline-operators-and-indexes

正如@Alex-Blex 建议的那样,我尝试了排序,但出现 OOM 错误:

The following error occurred while attempting to execute the aggregate query

Mongo Server error (MongoCommandException): Command failed with error 16819: 'Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in.' on server server-address:port. 

The full response is:
{ 

    "ok" : 0.0, 

    "errmsg" : "Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in.", 

    "code" : NumberInt(16819), 

    "codeName" : "Location16819"

}

我的错,我在错误的集合上尝试了它...添加与索引相同的排序,现在它正在使用索引。还没想好,花了大约 10 秒才给我结果。

新的解释:

{ 
    "stages" : [
        {
            "$cursor" : {
                "query" : {

                }, 
                "sort" : {
                    "dataSourceName" : NumberInt(1), 
                    "version" : NumberInt(1)
                }, 
                "fields" : {
                    "dataSourceName" : NumberInt(1), 
                    "version" : NumberInt(1), 
                    "_id" : NumberInt(0)
                }, 
                "queryPlanner" : {
                    "plannerVersion" : NumberInt(1), 
                    "namespace" : "....", 
                    "indexFilterSet" : false, 
                    "parsedQuery" : {

                    }, 
                    "winningPlan" : {
                        "stage" : "PROJECTION", 
                        "transformBy" : {
                            "dataSourceName" : NumberInt(1), 
                            "version" : NumberInt(1), 
                            "_id" : NumberInt(0)
                        }, 
                        "inputStage" : {
                            "stage" : "IXSCAN", 
                            "keyPattern" : {
                                "dataSourceName" : NumberInt(1), 
                                "version" : NumberInt(1)
                            }, 
                            "indexName" : "dataSourceName_1_version_1", 
                            "isMultiKey" : false, 
                            "multiKeyPaths" : {
                                "dataSourceName" : [

                                ], 
                                "version" : [

                                ]
                            }, 
                            "isUnique" : false, 
                            "isSparse" : false, 
                            "isPartial" : false, 
                            "indexVersion" : NumberInt(2), 
                            "direction" : "forward", 
                            "indexBounds" : {
                                "dataSourceName" : [
                                    "[MinKey, MaxKey]"
                                ], 
                                "version" : [
                                    "[MinKey, MaxKey]"
                                ]
                            }
                        }
                    }, 
                    "rejectedPlans" : [

                    ]
                }
            }
        }, 
        {
            "$group" : {
                "_id" : {
                    "dataSourceName" : "$dataSourceName", 
                    "version" : "$version"
                }
            }
        }
    ], 
    "ok" : 1.0
}

【问题讨论】:

    标签: mongodb aggregate-functions


    【解决方案1】:

    你所指的页面正好相反:

    $match 和 $sort 管道运算符可以利用索引

    你的第一个阶段是$group,既不是$match也不是$sort

    尝试在第一阶段对其进行排序以触发使用索引:

    db.getCollection("the-collection").aggregate(
        [
            { $sort: { dataSourceName:1, version:1 } },
            { 
                "$group" : {
                    "_id" : {
                        "dataSourceName" : "$dataSourceName", 
                        "version" : "$version"
                    }
                }
            }
        ], 
        { 
            "allowDiskUse" : false
        }
    );
    

    请注意,它应该是具有相同字段和排序的单个复合索引:

    db.getCollection("the-collection").createIndex({ dataSourceName:1, version:1 })
    

    【讨论】:

    • @appl3r,是的,因为聚合发明者 jira.mongodb.org/browse/… 建议使用排序技巧,试试看。
    • 是的,100Mb 对您的收藏来说应该太低了。尝试允许磁盘使用。使用 collscan 仍然可能比 40 秒快。
    • 不,我犯了一个错误,并在错误的集合上尝试了它。不过仍然不是很快。花了10秒:(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    相关资源
    最近更新 更多