加速MongoDB聚合操作

3

我有一个分片集合 "my_collection",其结构如下:

{ 
   "CREATED_DATE" : ISODate(...),
   "MESSAGE" : "Test Message",
   "LOG_TYPE": "EVENT"
}

mongoDB的环境分为2个分片。上述集合使用哈希分片键在LOG_TYPE上进行分片。 LOG_TYPE属性还有其他7种可能性。

我有100万条记录存储在“my_collection”中,并且我正在尝试使用以下查询找到基于LOG_TYPE的记录数:

db.my_collection.aggregate([
    { "$group" :{ 
        "_id": "$LOG_TYPE",
        "COUNT": { "$sum":1 }
    }}
])

但是这个查询大约需要3秒钟才能得到结果,有没有什么方法可以提高速度?另外,当我运行explain命令时,它显示没有使用索引。难道group命令不使用索引吗?


由于您实际上在聚合集合中的所有文档,因此索引将是无用的,除非它是生成覆盖查询的索引,但我不确定这是否有帮助。MongoDB仍将执行分散和收集操作。 - Sammaye
@Sammaye 这不正确。如果这是第一个且唯一的阶段,那么索引将被选择。请查看可用的“explain”输出(实际上从2.4.8开始),但从总体原则来看,优化器会解决这个问题。出于同样的原因,您给出的答案实际上并没有进一步优化该过程。如果您了解代码的工作方式,则优化器中隐含了投影。 - Neil Lunn
@NeilLunn,我已经提供了2.4.9的解释。 - Sammaye
@Sammaye 是的,这就是确切的表示方式。MESSAGE 属性不会超过150个字符。 - Sarath Nair
我认为这是一个网络问题,这个网络是哪个? - Sammaye
显示剩余4条评论
2个回答

8

目前,在提高查询性能方面聚合框架还存在一些限制,但您可以通过以下方式来提供帮助:

db.my_collection.aggregate([
    { "$sort" : { "LOG_TYPE" : 1 } },
    { "$group" :{ 
        "_id": "$LOG_TYPE",
        "COUNT": { "$sum":1 }
    }}
])

通过在LOG_TYPE上添加排序,您将“强制”优化器使用LOG_TYPE索引以按顺序获取文档。这将在多个方面提高性能,但具体取决于所使用的版本。
如果实际数据在$group阶段已排序,则可以提高累加总数的效率。您可以查看不同的查询计划,其中使用$sort将使用分片键索引。实际性能的改进取决于每个“桶”中值的数量 - 通常,LOG_TYPE仅具有七个不同的值,使其成为极差的分片键,但这意味着以下代码很可能比甚至是经过优化的聚合更快:
db.my_collection.distinct("LOG_TYPE").forEach(function(lt) {
   print(db.my_collection.count({"LOG_TYPE":lt});
});

0

MongoDB 中可以做的事情是有限的,最终这可能是一个超出 MongoDB 本身的物理问题,也许是延迟导致 configsrvs 响应不及时或者从分片中返回结果太慢。

然而,您可以通过使用覆盖查询来解决一些性能问题。由于您实际上正在对 LOG_TYPE 进行分片,因此您已经在其上拥有索引(在进行分片之前必须要求),不仅如此,聚合框架还会自动添加 projection,所以这并没有帮助。

MongoDB 可能需要与每个分片通信以获取结果,否则称为散布和收集操作。

$group 本身不会使用索引。

这是我在 2.4.9 上的结果:

> db.t.ensureIndex({log_type:1})
> db.t.runCommand("aggregate", {pipeline: [{$group:{_id:'$log_type'}}], explain: true})
{
        "serverPipeline" : [
                {
                        "query" : {

                        },
                        "projection" : {
                                "log_type" : 1,
                                "_id" : 0
                        },
                        "cursor" : {
                                "cursor" : "BasicCursor",
                                "isMultiKey" : false,
                                "n" : 1,
                                "nscannedObjects" : 1,
                                "nscanned" : 1,
                                "nscannedObjectsAllPlans" : 1,
                                "nscannedAllPlans" : 1,
                                "scanAndOrder" : false,
                                "indexOnly" : false,
                                "nYields" : 0,
                                "nChunkSkips" : 0,
                                "millis" : 0,
                                "indexBounds" : {

                                },
                                "allPlans" : [
                                        {
                                                "cursor" : "BasicCursor",
                                                "n" : 1,
                                                "nscannedObjects" : 1,
                                                "nscanned" : 1,
                                                "indexBounds" : {

                                                }
                                        }
                                ],
                                "server" : "ubuntu:27017"
                        }
                },
                {
                        "$group" : {
                                "_id" : "$log_type"
                        }
                }
        ],
        "ok" : 1
}

这是2.6的结果。
> use gthtg
switched to db gthtg
> db.t.insert({log_type:"fdf"})
WriteResult({ "nInserted" : 1 })
> db.t.ensureIndex({log_type: 1})
{ "numIndexesBefore" : 2, "note" : "all indexes already exist", "ok" : 1 }
> db.t.runCommand("aggregate", {pipeline: [{$group:{_id:'$log_type'}}], explain: true})
{
        "stages" : [
                {
                        "$cursor" : {
                                "query" : {

                                },
                                "fields" : {
                                        "log_type" : 1,
                                        "_id" : 0
                                },
                                "plan" : {
                                        "cursor" : "BasicCursor",
                                        "isMultiKey" : false,
                                        "scanAndOrder" : false,
                                        "allPlans" : [
                                                {
                                                        "cursor" : "BasicCursor",
                                                        "isMultiKey" : false,
                                                        "scanAndOrder" : false
                                                }
                                        ]
                                }
                        }
                },
                {
                        "$group" : {
                                "_id" : "$log_type"
                        }
                }
        ],
        "ok" : 1
}

新增的项目操作降低了性能。现在需要多约0.5秒的时间。我想就像你所说的,mongoDB必须执行分散和聚集操作,因此无法针对组操作使用索引。 - Sarath Nair
@cj0809 投影实际上是从您选择的输出属性中自动进行优化。额外的传递实际上会强制执行完整的传递,尽管您的集合或优化的工作集已经减少到所需的工作集,因此除非您已经将其减少到所需的工作集,否则不是一个好主意。 - Neil Lunn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接