2013-05-09 63 views
6

我有一個名爲post的MongoDB集合與3500萬對象。該集合有兩個二級索引,定義如下。多鍵索引慢範圍查詢

> db.post.getIndexKeys() 
[ 
    { 
     "_id" : 1 
    }, 
    { 
     "namespace" : 1, 
     "domain" : 1, 
     "post_id" : 1 
    }, 
    { 
     "namespace" : 1, 
     "post_time" : 1, 
     "tags" : 1 // this is an array field 
    } 
] 

我期待下面的查詢,它只是通過namespacepost_time過濾器,在合理的時間內沒有掃描所有對象上運行。

>db.post.find({post_time: {"$gte" : ISODate("2013-04-09T00:00:00Z"), "$lt" : ISODate("2013-04-09T01:00:00Z")}, namespace: "my_namespace"}).count() 
7408 

但是,它需要MongoDB的至少十分鐘,檢索結果和,奇怪的是,它管理掃描對象根據explain函數來完成這項工作。

> db.post.find({post_time: {"$gte" : ISODate("2013-04-09T00:00:00Z"), "$lt" : ISODate("2013-04-09T01:00:00Z")}, namespace: "my_namespace"}).explain() 
{ 
    "cursor" : "BtreeCursor namespace_1_post_time_1_tags_1", 
    "isMultiKey" : true, 
    "n" : 7408, 
    "nscannedObjects" : 69999186, 
    "nscanned" : 69999186, 
    "nscannedObjectsAllPlans" : 69999186, 
    "nscannedAllPlans" : 69999186, 
    "scanAndOrder" : false, 
    "indexOnly" : false, 
    "nYields" : 378967, 
    "nChunkSkips" : 0, 
    "millis" : 290048, 
    "indexBounds" : { 
     "namespace" : [ 
      [ 
       "my_namespace", 
       "my_namespace" 
      ] 
     ], 
     "post_time" : [ 
      [ 
       ISODate("2013-04-09T00:00:00Z"), 
       ISODate("292278995-01--2147483647T07:12:56.808Z") 
      ] 
     ], 
     "tags" : [ 
      [ 
       { 
        "$minElement" : 1 
       }, 
       { 
        "$maxElement" : 1 
       } 
      ] 
     ] 
    }, 
    "server" : "localhost:27017" 
} 

對象的數目和掃描的次數之間的差必須由標籤陣列(其均等於2)的長度而引起的。不過,我不明白爲什麼post_time過濾器不利用索引。

你能告訴我我可能會錯過什麼嗎?

(我工作的下降機器上24個核心和96 GB的RAM,我使用MongoDB的2.2.3。)

+0

命名空間的基數是否已經很低? – Sammaye 2013-05-09 11:20:05

+0

目前,只有一個不同的'namespace'值,這是我正在使用的值。 – 2013-05-09 11:20:59

+0

是的,這就是爲什麼,MongoDB必須首先限制第一個字段,因此它獲得所有'my_namespace',然後獲取該日期之間的所有文檔等等,嘗試重新排序索引以便post_time是第一個 – Sammaye 2013-05-09 11:21:50

回答

3

發現我在這個問題的答案:Order of $lt and $gt in MongoDB range query

我的索引是一個多鍵索引(在tags),我正在運行範圍查詢(在post_time)。 Apparently,在這種情況下,MongoDB不能使用範圍的兩邊作爲過濾器,所以它只是選擇$gte子句,它首先出現。由於我的下限恰好是最低值post_time,MongoDB開始掃描所有對象。

不幸的是,這不是整個故事。爲了解決這個問題,我也創建了非多鍵索引,但是MongoDB堅持使用壞索引。這讓我覺得問題在別處。最後,我不得不放棄多鍵索引,並創建一個沒有tags字段。現在一切都很好。

+0

Dang我從來不知道'$ gt'和'$ lt'和miltikeys,很好找! – Sammaye 2013-05-09 12:32:58

+0

使用cursor.hint也可能是一個解決方案,使mongodb使用其他索引 (http://docs.mongodb.org/manual/reference/method/cursor.hint/#cursor.hint) – rudi 2014-09-19 14:49:39