【问题标题】:How to optimize mongoDB query?如何优化 mongoDB 查询?
【发布时间】:2014-06-13 12:16:11
【问题描述】:

我在 mongoDB 中有以下示例文档。

  {
    "location" : {
                "language" : null,
                "country" : "null",
                "city" : "null",
                "state" : null,
                "continent" : "null",
                "latitude" : "null",
                "longitude" : "null"
         },
    "request" : [
                 {
                  "referrer" : "direct",
                  "url" : "http://www.google.com/"
                  "title" : "index page"
                  "currentVisit" : "1401282897"
                  "visitedTime" : "1401282905"
                 },

                 {
                 "referrer" : "direct",
                 "url" : "http://www.stackoverflow.com/",
                 "title" : "index page"
                 "currentVisit" : "1401282900"
                 "visitedTime" : "1401282905"
                 },
           ......
               ]
    "uuid" : "109eeee0-e66a-11e3"
}

注意:

  1. 数据库包含多个10845文档
  2. 每个文档都包含近乎100的请求(请求数组中有100个对象)。
  3. 技术/语言 - node.js

  4. 我让setProfiling检查执行时间

    First Query - 13899ms
    Second Query - 9024ms 
    Third Query - 8310ms
    Fourth Query - 6858ms
    
  5. 使用索引没有太大区别

查询:

我要执行以下aggregation queries 来获取数据。

 var match = {"request.currentVisit":{$gte:core.getTime()[1].toString(),$lte:core.getTime()[0].toString()}};

For Example: var match = {"request.currentVisit":{$gte:"1401282905",$lte:"1401282935"}};

对于第三和第四个查询 request.visitedTime 而不是 request.currentVisit

  1. 第一

    [
        { "$project":{
            "request.currentVisit":1,
            "request.url":1
        }},
       { "$match":{
           "request.1": {$exists:true}
       }},
       { "$unwind": "$request" },
       { "$match": match },
       { "$group": { 
           "_id": {
               "url":"$request.url"
           },
           "count": { "$sum": 1 }
       }},
       { "$sort":{ "count": -1 } }
    ]
    
  2. 第二个

    [
        { "$project": {
            "request.currentVisit":1,
            "request.url":1
        }},
        { "$match": {  
            "request":{ "$size": 1 }
        }},
        { "$unwind": "$request" },
        { "$match": match },
        { "$group": {
            "_id":{ 
                "url":"$request.url"
            },
            "count":{ "$sum": 1 }
        }},
        { "$sort": { "count": -1} }
    ]
    
  3. 第三

    [
        { "$project": {
             "request.visitedTime":1,
             "uuid":1
        }},
        { "$match":{
            "request.1": { "$exists": true } 
        }},
        { "$match": match },
        { "$group": {
             "_id": "$uuid",
             "count":{ "$sum": 1 }
        }},
        { "$group": {
            "_id": null,
            "total": { "$sum":"$count" }}
        }}
    ]
    
  4. 第四

    [
        { "$project": {
            "request.visitedTime":1,
            "uuid":1
        }},
        { "$match":{
            "request":{ "$size": 1 }
        }},
        { "$match": match },
        { "$group": {
           "_id":"$uuid",
           "count":{ "$sum": 1 }
       }},
       { "$group": {
           "_id":null,
           "total": { "$sum": "$count" }
       }}
    ]
    

问题:

获取数据的时间超过了38091 ms

有什么办法可以优化查询吗?

任何建议将不胜感激。

【问题讨论】:

  • 您是否尝试在request 子文档上创建索引?确定它是否适用于您的应用程序。
  • 你能给我们解释一下吗?
  • 我其实只是想知道match 变量的秘密究竟是什么。这些查询中存在明显的问题,这些问题很容易回答,但在不了解细节的情况下并不完整。
  • @NeilLunn 我添加了匹配过滤器
  • 我在问match 管道阶段内的match 变量内的条件是什么。这样我们就知道你的实际情况是什么。它没有在你上面的代码中列出

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

嗯,有一些问题,你肯定需要索引,但你不能有复合的。它是您在要索引的数组中查询的“时间戳”值。还建议您将这些转换为数值而不是当前字符串,或者实际上转换为 BSON 日期类型。后一种形式实际上在内部存储为数字时间戳值,因此一般会减少存储大小,这也减少了索引大小以及更有效地匹配数字值。

每个查询的最大问题是,您总是在处理 $unwind 之后再深入“数组”内容,然后使用匹配“过滤”它。虽然这是您想要为结果执行的操作,但由于您没有在早期阶段应用相同的过滤器,因此当您 $unwind 时,管道中有许多与这些条件不匹配的文档。结果是您不需要在此阶段处理的“大量”文档。而且在这里你不能使用索引。

您需要这种匹配的地方是在管道阶段的开始。在过滤该实际数组之前,这会将文档范围缩小到“可能的”匹配项。

所以以第一个为例:

[
   { "$match":{
       { "request.currentVisit":{ 
           "$gte":"1401282905", "$lte": "1401282935"
       }
   }},
   { "$unwind": "$request" },
   { "$match":{
       { "request.currentVisit":{ 
           "$gte":"1401282905", "$lte": "1401282935"
       }
   }},
   { "$group": { 
       "_id": {
           "url":"$request.url"
       },
       "count": { "$sum": 1 }
   }},
   { "$sort":{ "count": -1 } }
]

所以做了一些改变。管道的头部有一个$match。这缩小了文档范围并能够使用索引。这是最重要的性能考虑。黄金法则,始终首先“匹配”。

您在其中的$project 是多余的,因为您不能“仅”投影尚未展开的数组的字段。还有一种误解是人们认为他们$project首先是为了减少管道。如果实际上有一个稍后的 $project$group 语句实际上限制了字段,那么效果非常小,那么这将是“前向优化”的,所以事情确实会为您从管道处理中取出。上面的$match 语句仍然可以进行更多优化。

不再需要查看数组是否与另一个 $match 阶段一起实际存在,因为您现在“隐式”在管道开始处执行此操作。如果更多条件让您更舒服,则将它们添加到初始管道阶段。

其余部分保持不变,因为您随后 $unwind 数组和 $match 在继续进行剩余处理之前过滤您实际需要的项目。到现在为止,输入文档已经显着减少了,或者说已经减少了。

您可以使用 MongoDB 2.6 及更高版本执行的另一种选择是在您甚至 **$unwind 之前“过滤”数组内容。这将产生如下列表:

[
   { "$match":{
       { "request.currentVisit":{ 
           "$gte":"1401282905", "$lte": "1401282935"
       }
   }},
   { "$project": {
       "request": {
           "$setDifference": [
               { 
                   "$map": {
                       "input": "$request",
                       "as": "el",
                       "in": {
                           "$cond"": [
                               {
                                   "$and":[
                                       { "$gte": [ "1401282905", "$$el.currentVisit" ] },
                                       { "$lt": [ "1401282935", "$$el.currentVisit" ] }
                                   ]
                               }
                               "$el",
                               false
                           ]
                       }
                   }
               }
               [false]
           ]
       }
   }}
   { "$unwind": "$request" },
   { "$group": { 
       "_id": {
           "url":"$request.url"
       },
       "count": { "$sum": 1 }
   }},
   { "$sort":{ "count": -1 } }
]

这可以通过在$unwind 之前“过滤”数组来为您节省一些费用,这可能比在之后执行$match 更好。

但这是您所有陈述的一般规则。你需要可用的索引并且你需要$matchfirst

您真正想要的实际结果可能会在单个查询中获得,但就目前而言,您的问题并不是这样呈现的。尝试按照概述更改您的处理,您应该会看到显着的改进。

如果您仍然试图接受这可能是单数的,那么您可以随时提出另一个问题。

【讨论】:

  • 感谢您的解释。这是工作。性能大幅提升
猜你喜欢
  • 2015-01-21
  • 2015-01-09
  • 2015-03-06
  • 1970-01-01
  • 1970-01-01
  • 2022-09-30
  • 1970-01-01
  • 1970-01-01
  • 2022-11-13
相关资源
最近更新 更多