2014-01-28 147 views
2

我在MongoDB中有一個約有350K文件的集合。我有Updated(降序)和SecondaryCategories(升序)的複合指數。

db.Content.ensureIndex({ "Updated" : -1, "SecondaryCategories" : 1 },{ "name" : "Updated_SecondaryCategories", "background" : true }); 

我使用MongoDB的C#驅動程序使用lambda語法來構建查詢:

IQueryable<Content> query = repo.GetAll() 
      .Where(
       x => 
        x.SecondaryCategories.ContainsAny(sel) && 
        (x.Type == ContentType.News || x.Type == ContentType.FotoNews || x.Type == ContentType.LinkedNews || x.Type == ContentType.MediaNews) && 
        x.Texts.Any(y => (int)y.Language == cultureId)) 
      .OrderByDescending(x => x.Updated) 
      .Skip(skipItems) 
      .Take(count); 

和我得到以下幾點:

db.Content.find({ "$query" : { "SecondaryCategories" : { "$in" : [524, 615, 550, 546, 552, 617, 547, 549, 548, 613, 614, 551, 618, 545] }, "$or" : [{ "Type" : 4 }, { "Type" : 8 }, { "Type" : 32 }, { "Type" : 16 }], "Texts" : { "$elemMatch" : { "Language" : 0 } } }, $orderby: { Updated: -1 }}).limit(20); 

查詢大約爲運行1300ms這很慢。現在,當我刪除$query經營者,我應該得到以下幾點:

db.Content.find({ "SecondaryCategories" : { "$in" : [524, 615, 550, 546, 552, 617, 547, 549, 548, 613, 614, 551, 618, 545] }, "$or" : [{ "Type" : 4 }, { "Type" : 8 }, { "Type" : 32 }, { "Type" : 16 }], "Texts" : { "$elemMatch" : { "Language" : 0 } }}).sort({"Updated" : -1}).limit(20); 

該查詢只在1ms內運行。

解釋第一個查詢(用$query運營商):

db.Content.find({ "$query" : { "SecondaryCategories" : { "$in" : [524, 615, 550, 546, 552, 617, 547, 549, 548, 613, 614, 551, 618, 545] }, "$or" : [{ "Type" : 4 }, { "Type" : 8 }, { "Type" : 32 }, { "Type" : 16 }], "Texts" : { "$elemMatch" : { "Language" : 0 } } }, $orderby: { Updated: -1 }, $explain: 1 }).limit(20).pretty(); 
{ 
    "cursor" : "BtreeCursor Updated_SecondaryCategories multi", 
    "isMultiKey" : true, 
    "n" : 188173, 
    "nscannedObjects" : 188668, 
    "nscanned" : 337619, 
    "nscannedObjectsAllPlans" : 189056, 
    "nscannedAllPlans" : 338007, 
    "scanAndOrder" : false, 
    "indexOnly" : false, 
    "nYields" : 1, 
    "nChunkSkips" : 0, 
    "millis" : 1304, 
    "indexBounds" : { 
      "Updated" : [ 
        [ 
          { 
            "$maxElement" : 1 
          }, 
          { 
            "$minElement" : 1 
          } 
        ] 
      ], 
      "SecondaryCategories" : [ 
        [ 
          524, 
          524 
        ], 
        [ 
          545, 
          545 
        ], 
        [ 
          546, 
          546 
        ], 
        [ 
          547, 
          547 
        ], 
        [ 
          548, 
          548 
        ], 
        [ 
          549, 
          549 
        ], 
        [ 
          550, 
          550 
        ], 
        [ 
          551, 
          551 
        ], 
        [ 
          552, 
          552 
        ], 
        [ 
          613, 
          613 
        ], 
        [ 
          614, 
          614 
        ], 
        [ 
          615, 
          615 
        ], 
        [ 
          617, 
          617 
        ], 
        [ 
          618, 
          618 
        ] 
      ] 
    } 

而對於第二(不$query運營商):

db.Content.find({ "SecondaryCategories" : { "$in" : [524, 615, 550, 546, 552, 617, 547, 549, 548, 613, 614, 551, 618, 545] }, "$or" : [{ "Type" : 4 }, { "Type" : 8 }, { "Type" : 32 }, { "Type" : 16 }], "Texts" : { "$elemMatch" : { "Language" : 0 } }}).sort({"Updated" : -1}).limit(20).explain(); 
{ 
    "cursor" : "BtreeCursor Updated_SecondaryCategories multi", 
    "isMultiKey" : true, 
    "n" : 20, 
    "nscannedObjects" : 29, 
    "nscanned" : 69, 
    "nscannedObjectsAllPlans" : 94, 
    "nscannedAllPlans" : 134, 
    "scanAndOrder" : false, 
    "indexOnly" : false, 
    "nYields" : 0, 
    "nChunkSkips" : 0, 
    "millis" : 0, 
    "indexBounds" : { 
      "Updated" : [ 
        [ 
          { 
            "$maxElement" : 1 
          }, 
          { 
            "$minElement" : 1 
          } 
        ] 
      ], 
      "SecondaryCategories" : [ 
        [ 
          524, 
          524 
        ], 
        [ 
          545, 
          545 
        ], 
        [ 
          546, 
          546 
        ], 
        [ 
          547, 
          547 
        ], 
        [ 
          548, 
          548 
        ], 
        [ 
          549, 
          549 
        ], 
        [ 
          550, 
          550 
        ], 
        [ 
          551, 
          551 
        ], 
        [ 
          552, 
          552 
        ], 
        [ 
          613, 
          613 
        ], 
        [ 
          614, 
          614 
        ], 
        [ 
          615, 
          615 
        ], 
        [ 
          617, 
          617 
        ], 
        [ 
          618, 
          618 
        ] 
      ] 
    } 

我實在無法理解查詢這種行爲差異。它似乎是第一個查詢掃描通過338k文件,並返回188173,這是有限的,而第二個掃描只有69.

我的索引是否錯誤或我必須重寫查詢?如果沒有使用C#MongoDB Driver的$query運算符,是否有任何方法來編寫查詢?

+0

你試過解釋查詢嗎? http://docs.mongodb.org/manual/reference/operator/meta/query/#op._S_query - 我想你可能需要添加限制條款的查詢文件本身,而不是'.limit' –

+0

是的,我使用'$ explain:1'來解釋第一個查詢。我找不到任何其他限制方式。有'$ maxScan',但它只是限制掃描的文件數量。 – Villu89

回答

0
mongodb docs

的狀態

不要混合使用的查詢表格。如果使用$查詢格式,請不要將 遊標方法附加到find()。要修改查詢,請使用meta-query 運算符,如$ explain。

所以在你的情況下,我認爲這是.limit(20)的使用,它被省略並導致你的查詢繼續查看所有文檔的所有集合。

也許這是c#驅動程序上的一個錯誤。

+0

我無法爲'limit()'找到任何元查詢運算符。有'$ maxScan'操作符,但根據手冊:'限制查詢只在執行查詢時掃描指定數量的文檔。所以它只會阻止Mongo在整個索引競賽中掃描超過指定數量的文檔。我永遠無法確定它是否會返回我需要的20個文檔。 – Villu89

+0

我沒有看到它......我只想指出,它是影響性能的「極限」使用(或不)。嘗試從兩個查詢中刪除它並查看其差異 – xlembouras

0

好了,因爲沒有爲limit()$maxScan元查詢運營商有不同的含義,我就只能把條件對更新的領域,例如比上個月度:"Updated" : {$gte : new ISODate("2013-12-29T00:00:01Z")}。這樣我的查詢將在50ms內返回。如果返回的文檔少於限制,我可以擴展日期篩選器並再次運行查詢。