Explain
從之前的文章中,我們可以知道explain()能夠提供大量與查詢相關的信息。對於速度比較慢的查詢來說,這是最重要的診斷工具之一。通過查看一個查詢的explain()輸出信息,可以知道查詢使用了哪個索引,以及是如何使用的。
最常見的explain()的輸出有兩種類型:使用索引的查詢和沒有使用索引的查詢。
在上一篇MongoDB的博客可以看到兩種類型的explain如下:
1. 沒有使用索引時
{ "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1001, "nscanned" : 1001, "nscannedObjectsAllPlans" : 1001, "nscannedAllPlans" : 1001, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 7, "nChunkSkips" : 0, "millis" : 1, "server" : "user:27017", "filterSet" : false }
2. 使用索引時
{ "cursor" : "BtreeCursor username_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1, "nscanned" : 1, "nscannedObjectsAllPlans" : 1, "nscannedAllPlans" : 1, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "username" : [ [ "user1000", "user1000" ] ] }, "server" : "user:27017", "filterSet" : false }
我們以有索引的結果為例,來依次看一下這些字段代表的意思
-
"cursor" : "BtreeCursor username_1"
BtreeCursor表示本次查詢使用了索引,具體來說,是使用了“username”上的索引{“username”:1}。如果查詢要對結果進行逆序遍歷,或者使用了多鍵索引,就可以在這個字段中看到“reverse”和“multi”這樣的值。 -
"isMultiKey" : false
用於說明本次是否使用了多鍵索引。 -
"n" : 1
本次查詢返回的文檔數量 -
"nscannedObjects" : 1
這是MongoDB按照索引指針去磁盤上查找實際文檔的次數。如果查詢包含的查詢條件不是索引的一部分,或者說要求返回不在索引內的字段,MongoDB就必須依次查找每個索引條目指向的文檔。 -
"nscanned" : 1
如果有使用索引,那么這個數字就是查找過的索引條目數量,如果本次查詢是一次全表掃描,那么這個數字就代表檢查過的文檔數目。 -
"scanAndOrder" : false
MongoDB是否在內存中對結果集進行了排序 -
"indexOnly" : false
MongoDB是否只使用索引就能完成此次查詢。在本例中,MongoDB只使用索引就找到了全部的匹配文檔,從“nscanned”和“n”相等就可以看出來。然而,本次查詢要就返回匹配文檔中的所有字段,而索引只包含“username”這個字段,如果就本次查詢修改為{"_id":0, "username":1},那么本次查詢就可以被索引覆蓋了,"indexOnly"的值就會是true。 -
"nYields" : 0
為了讓寫入請求能夠順利執行,本次查詢暫停暫停的次數。如果有寫入請求需求處理,查詢會周期性的釋放他們的鎖,以便寫入能夠順利執行。然而,在本次查詢中,沒有寫入請求,因此查詢沒有暫停過。 -
"millis" : 0
數據庫執行本次查詢所耗費的毫秒數。這個數字越小,說明效率越高。 -
"indexBounds" : {...}
這個字段描述了索引的使用情況,給出了索引的遍歷范圍。由於此次查詢是精確匹配,所以所以只要查“user1000”這個值就可以了。
Hint
雖然MongoDB查詢優化器一般工作的很不錯,但是也可以使用hint()來強迫MongoDB使用一個特定的索引。在這種方法下某些情形下會提升性能。一個有索引的collection並且執行一個多字段的查詢。傳入一個制定的索引,強迫查詢使用該索引。
db.users.find({"username":"user1000", "age":30}).hint({"username":1})
注意:請確定你已經創建了相應的索引。
假設在users上有個{"a": 1, "b": 1}的索引,名稱是"a_1_b_1",則如下兩種方式等價:
db.users.find({"a": 4, "b": 5, "c": 6}).hint({"a": 1, "b": 1})
db.users.find({"a": 4, "b": 5, "c": 6}).hint("a_1_b_1")
也可以強迫查詢不適用索引,做表掃描:
db.users.find().hint({"$natural":1})