摘要:表示本次查詢使用了索引,具體來(lái)說(shuō),是使用了和上的索引,。建立索引時(shí),或者是每執(zhí)行次查詢之后,查詢優(yōu)化器都會(huì)重新評(píng)估查詢計(jì)劃。上一篇文章指南使用復(fù)合索引操作符如何使用索引索引對(duì)象和數(shù)組索引基數(shù)下一篇文章指南索引類型
上一篇文章:MongoDB指南---11、使用復(fù)合索引、$操作符如何使用索引、索引對(duì)象和數(shù)組、索引基數(shù)使用explain()和hint()
下一篇文章:MongoDB指南---13、索引類型
從上面的內(nèi)容可以看出,explain()能夠提供大量與查詢相關(guān)的信息。對(duì)于速度比較慢的查詢來(lái)說(shuō),這是最重要的診斷工具之一。通過(guò)查看一個(gè)查詢的explain()輸出信息,可以知道查詢使用了哪個(gè)索引,以及是如何使用的。對(duì)于任意查詢,都可以在最后添加一個(gè)explain()調(diào)用(與調(diào)用sort()或者limit()一樣,不過(guò)explain()必須放在最后)。
最常見的explain()輸出有兩種類型:使用索引的查詢和沒有使用索引的查詢。對(duì)于特殊類型的索引,生成的查詢計(jì)劃可能會(huì)有些許不同,但是大部分字段都是相似的。另外,分片返回的是多個(gè)explain()的聚合(第13章會(huì)介紹),因?yàn)椴樵儠?huì)在多個(gè)服務(wù)器上執(zhí)行。
不使用索引的查詢的exlpain()是最基本的explain()類型。如果一個(gè)查詢不使用索引,是因?yàn)樗褂昧?BasicCursor"(基本游標(biāo))。反過(guò)來(lái)說(shuō),大部分使用索引的查詢使用的是BtreeCursor(某些特殊類型的索引,比如地理空間索引,使用的是它們自己類型的游標(biāo))。
對(duì)于使用了復(fù)合索引的查詢,最簡(jiǎn)單情況下的explain()輸出如下所示:
> db.users.find({"age" : 42}).explain() { "cursor" : "BtreeCursor age_1_username_1", "isMultiKey" : false, "n" : 8332, "nscannedObjects" : 8332, "nscanned" : 8332, "nscannedObjectsAllPlans" : 8332, "nscannedAllPlans" : 8332, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 91, "indexBounds" : { "age" : [ [ 42, 42 ] ], "username" : [ [ { "$minElement" : 1 }, { "$maxElement" : 1 } ] ] }, "server" : "ubuntu:27017" }
從輸出信息中可以看到它使用的索引是age_1_username_1。"millis"表明了這個(gè)查詢的執(zhí)行速度,時(shí)間是從服務(wù)器收到請(qǐng)求開始一直到發(fā)出響應(yīng)為止。然而,這個(gè)數(shù)值不一定真的是你希望看到的值。如果MongoDB嘗試了多個(gè)查詢計(jì)劃,那么"millis"顯示的是這些查詢計(jì)劃花費(fèi)的總時(shí)間,而不是最優(yōu)查詢計(jì)劃所花的時(shí)間。
接下來(lái)是實(shí)際返回的文檔數(shù)量:"n"。它無(wú)法反映出MongoDB在執(zhí)行這個(gè)查詢的過(guò)程中所做的工作:搜索了多少索引條目和文檔。索引條目是使用"nscanned"描述的。"nscannedObjects"字段的值就是所掃描的文檔數(shù)量。最后,如果要對(duì)結(jié)果集進(jìn)行排序,而MongoDB無(wú)法對(duì)排序使用索引,那么"scanAndOrder"的值就會(huì)是true。也就是說(shuō),MongoDB不得不在內(nèi)存中對(duì)結(jié)果進(jìn)行排序,這是非常慢的,而且結(jié)果集的數(shù)量要比較小。
現(xiàn)在你已經(jīng)知道這些基礎(chǔ)知識(shí)了,接下來(lái)依次詳細(xì)介紹這些字段。
"cursor" : "BtreeCursor age_1_username_1"
BtreeCursor表示本次查詢使用了索引,具體來(lái)說(shuō),是使用了"age"和"username"上的索引{"age" : 1, "username" : 1}。如果查詢要對(duì)結(jié)果進(jìn)行逆序遍歷,或者是使用了多鍵索引,就可以在這個(gè)字段中看到"reverse"和"multi"這樣的值。
"isMultiKey" : false
用于說(shuō)明本次查詢是否使用了多鍵索引(詳見5.1.4節(jié))。
"n" : 8332
本次查詢返回的文檔數(shù)量。
"nscannedObjects" : 8332
這是MongoDB按照索引指針去磁盤上查找實(shí)際文檔的次數(shù)。如果查詢包含的查詢條件不是索引的一部分,或者說(shuō)要求返回不在索引內(nèi)的字段,MongoDB就必須依次查找每個(gè)索引條目指向的文檔。
"nscanned" : 8332
如果有使用索引,那么這個(gè)數(shù)字就是查找過(guò)的索引條目數(shù)量。如果本次查詢是一次全表掃描,那么這個(gè)數(shù)字就表示檢查過(guò)的文檔數(shù)量。
"scanAndOrder" : false
MongoDB是否在內(nèi)存中對(duì)結(jié)果集進(jìn)行了排序。
"indexOnly" : false
MongoDB是否只使用索引就能完成此次查詢(詳見“覆蓋索引”部分)。
在本例中,MongoDB只使用索引就找到了全部的匹配文檔,從"nscanned"和"n"相等就可以看出來(lái)。然而,本次查詢要求返回匹配文檔中的所有字段,而索引只包含"age"和"username"兩個(gè)字段。如果將本次查詢修改為({"_id" : 0, "age" : 1, "username" : 1}),那么本次查詢就可以被索引覆蓋了,"indexOnly"的值就會(huì)是true。
"nYields" : 0
為了讓寫入請(qǐng)求能夠順利執(zhí)行,本次查詢暫停的次數(shù)。如果有寫入請(qǐng)求需要處理,查詢會(huì)周期性地釋放它們的鎖,以便寫入能夠順利執(zhí)行。然而,在本次查詢中,沒有寫入請(qǐng)求,因?yàn)椴樵儧]有暫停過(guò)。
"millis" : 91
數(shù)據(jù)庫(kù)執(zhí)行本次查詢所耗費(fèi)的毫秒數(shù)。這個(gè)數(shù)字越小,說(shuō)明查詢效率越高。
"indexBounds" : {...}
這個(gè)字段描述了索引的使用情況,給出了索引的遍歷范圍。由于查詢中的第一個(gè)語(yǔ)句是精確匹配,因此索引只需要查找42這個(gè)值就可以了。本次查詢沒有指定第二個(gè)索引鍵,因此這個(gè)索引鍵上沒有限制,數(shù)據(jù)庫(kù)會(huì)在"age"為42的條目中將用戶名介于負(fù)無(wú)窮("$minElement" : 1)和正無(wú)窮("$maxElement" : 1)的條目都找出來(lái)。
再來(lái)看一個(gè)稍微復(fù)雜點(diǎn)的例子:假如有一個(gè){"user name" : 1, "age" : 1}上的索引和一個(gè) {"age" : 1, "username" : 1}上的索引。同時(shí)查詢"username"和"age"時(shí),會(huì)發(fā)生什么情況?呃,這取決于具體的查詢:
> db.c.find({age : {$gt : 10}, username : "sally"}).explain() { "cursor" : "BtreeCursor username_1_age_1", "indexBounds" : [ [ { "username" : "sally", "age" : 10 }, { "username" : "sally", "age" : 1.7976931348623157e+308 } ] ], "nscanned" : 13, "nscannedObjects" : 13, "n" : 13, "millis" : 5 }
由于在要在"username"上執(zhí)行精確匹配,在"age"上進(jìn)行范圍查詢,因此,數(shù)據(jù)庫(kù)選擇使用{"username" : 1, "age" : 1}索引,這與查詢語(yǔ)句的順序相反。另一方面來(lái)說(shuō),如果需要對(duì)"age"精確匹配而對(duì)"username"進(jìn)行范圍查詢,MongoDB就會(huì)使用另一個(gè)索引:
> db.c.find({"age" : 14, "username" : /.*/}).explain() { "cursor" : "BtreeCursor age_1_username_1 multi", "indexBounds" : [ [ { "age" : 14, "username" : "" }, { "age" : 14, "username" : { } } ], [ { "age" : 14, "username" : /.*/ }, { "age" : 14, "username" : /.*/ } ] ], "nscanned" : 2, "nscannedObjects" : 2, "n" : 2, "millis" : 2 }
如果發(fā)現(xiàn)MongoDB使用的索引與自己希望它使用的索引不一致,可以使用hit()強(qiáng)制MongoDB使用特定的索引。例如,如果希望MongoDB在上個(gè)例子的查詢中使用{"username" : 1, "age" : 1}索引,可以這么做:
> db.c.find({"age" : 14, "username" : /.*/}).hint({"username" : 1, "age" : 1})
如果查詢沒有使用你希望它使用的索引,于是你使用hint強(qiáng)制MongoDB使用某個(gè)索引,那么應(yīng)該在應(yīng)用程序部署之前在所指定的索引上執(zhí)行explain()。如果強(qiáng)制MongoDB在某個(gè)查詢上使用索引,而這個(gè)查詢不知道如何使用這個(gè)索引,這樣會(huì)導(dǎo)致查詢效率降低,還不如不使用索引來(lái)得快。
查詢優(yōu)化器MongoDB的查詢優(yōu)化器與其他數(shù)據(jù)庫(kù)稍有不同?;緛?lái)說(shuō),如果一個(gè)索引能夠精確匹配一個(gè)查詢(要查詢"x",剛好在"x"上有一個(gè)索引),那么查詢優(yōu)化器就會(huì)使用這個(gè)索引。不然的話,可能會(huì)有幾個(gè)索引都適合你的查詢。MongoDB會(huì)從這些可能的索引子集中為每次查詢計(jì)劃選擇一個(gè),這些查詢計(jì)劃是并行執(zhí)行的。最早返回100個(gè)結(jié)果的就是勝者,其他的查詢計(jì)劃就會(huì)被中止。
這個(gè)查詢計(jì)劃會(huì)被緩存,這個(gè)查詢接下來(lái)都會(huì)使用它,直到集合數(shù)據(jù)發(fā)生了比較大的變動(dòng)。如果在最初的計(jì)劃評(píng)估之后集合發(fā)生了比較大的數(shù)據(jù)變動(dòng),查詢優(yōu)化器就會(huì)重新挑選可行的查詢計(jì)劃。建立索引時(shí),或者是每執(zhí)行1000次查詢之后,查詢優(yōu)化器都會(huì)重新評(píng)估查詢計(jì)劃。
explain()輸出信息里的"allPlans"字段顯示了本次查詢嘗試過(guò)的每個(gè)查詢計(jì)劃。
提取較小的子數(shù)據(jù)集時(shí),索引非常高效。也有一些查詢不使用索引會(huì)更快。結(jié)果集在原集合中所占的比例越大,索引的速度就越慢,因?yàn)槭褂盟饕枰M(jìn)行兩次查找:一次是查找索引條目,一次是根據(jù)索引指針去查找相應(yīng)的文檔。而全表掃描只需要進(jìn)行一次查找:查找文檔。在最壞的情況下(返回集合內(nèi)的所有文檔),使用索引進(jìn)行的查找次數(shù)會(huì)是全表掃描的兩倍,效率會(huì)明顯比全表掃描低很多。
可惜,并沒有一個(gè)嚴(yán)格的規(guī)則可以告訴我們,如何根據(jù)數(shù)據(jù)大小、索引大小、文檔大小以及結(jié)果集的平均大小來(lái)判斷什么時(shí)候索引很有用,什么時(shí)候索引會(huì)降低查詢速度(如表5-1所示)。一般來(lái)說(shuō),如果查詢需要返回集合內(nèi)30%的文檔(或者更多),那就應(yīng)該對(duì)索引和全表掃描的速度進(jìn)行比較。然而,這個(gè)數(shù)字可能會(huì)在2%~60%之間變動(dòng)。
表5-1 影響索引效率的屬性
索引通常適用的情況 | 全表掃描通常適用的情況 |
---|---|
集合較大 | 集合較小 |
文檔較大 | 文檔較小 |
選擇性查詢 | 非選擇性查詢 |
假如我們有一個(gè)收集統(tǒng)計(jì)信息的分析系統(tǒng)。應(yīng)用程序要根據(jù)給定賬戶去系統(tǒng)中查詢所有文檔,根據(jù)從初始一直到一小時(shí)之前的數(shù)據(jù)生成圖表:
> db.entries.find({"created_at" : {"$lt" : hourAgo}})
我們?cè)?created_at"上創(chuàng)建索引以提高查詢速度。
最初運(yùn)行時(shí),結(jié)果集非常小,可以立即返回。幾個(gè)星期過(guò)去以后,數(shù)據(jù)開始多起來(lái)了,一個(gè)月之后,這個(gè)查詢耗費(fèi)的時(shí)間越來(lái)越長(zhǎng)。
對(duì)于大部分應(yīng)用程序來(lái)說(shuō),這很可能就是那個(gè)“錯(cuò)誤的”查詢:真的需要在查詢中返回?cái)?shù)據(jù)集中的大部分內(nèi)容嗎?大部分應(yīng)用程序(尤其是擁有非常大的數(shù)據(jù)集的應(yīng)用程序)都不需要。然而,也有一些合理的情況,可能需要得到大部分或者全部的數(shù)據(jù):也許需要將這些數(shù)據(jù)導(dǎo)出到報(bào)表系統(tǒng),或者是放在批量任務(wù)中。在這些情況下,應(yīng)該盡可能快地返回?cái)?shù)據(jù)集中的內(nèi)容。
可以用{"$natural" : 1}強(qiáng)制數(shù)據(jù)庫(kù)做全表掃描。6.1節(jié)會(huì)介紹$natural,它可以指定文檔按照磁盤上的順序排列。特別地,$natural可以強(qiáng)制MongoDB做全表掃描:
> db.entries.find({"created_at" : {"$lt" : hourAgo}}).hint({"$natural" : 1})
使用"$natural"排序有一個(gè)副作用:返回的結(jié)果是按照磁盤上的順序排列的。對(duì)于一個(gè)活躍的集合來(lái)說(shuō),這是沒有意義的:隨著文檔體積的增加或者縮小,文檔會(huì)在磁盤上進(jìn)行移動(dòng),新的文檔會(huì)被寫入到這些文檔留下的空白位置。但是,對(duì)于只需要進(jìn)行插入的工作來(lái)說(shuō),如果要得到最新的(或者最早的)文檔,使用$natural就非常有用了。
上一篇文章:MongoDB指南---11、使用復(fù)合索引、$操作符如何使用索引、索引對(duì)象和數(shù)組、索引基數(shù)
下一篇文章:MongoDB指南---13、索引類型
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/19579.html
摘要:表示本次查詢使用了索引,具體來(lái)說(shuō),是使用了和上的索引,。建立索引時(shí),或者是每執(zhí)行次查詢之后,查詢優(yōu)化器都會(huì)重新評(píng)估查詢計(jì)劃。上一篇文章指南使用復(fù)合索引操作符如何使用索引索引對(duì)象和數(shù)組索引基數(shù)下一篇文章指南索引類型 上一篇文章:MongoDB指南---11、使用復(fù)合索引、$操作符如何使用索引、索引對(duì)象和數(shù)組、索引基數(shù)下一篇文章:MongoDB指南---13、索引類型 使用explain...
摘要:復(fù)合唯一索引也可以創(chuàng)建復(fù)合的唯一索引。中的稀疏索引與關(guān)系型數(shù)據(jù)庫(kù)中的稀疏索引是完全不同的概念。但是這里不會(huì)指明索引是否是多鍵索引。上一篇文章指南使用和何時(shí)不應(yīng)該使用索引下一篇文章指南特殊的索引和集合固定集合索引全文本索引 上一篇文章:MongoDB指南---12、使用explain()和hint()、何時(shí)不應(yīng)該使用索引下一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合...
摘要:復(fù)合唯一索引也可以創(chuàng)建復(fù)合的唯一索引。中的稀疏索引與關(guān)系型數(shù)據(jù)庫(kù)中的稀疏索引是完全不同的概念。但是這里不會(huì)指明索引是否是多鍵索引。上一篇文章指南使用和何時(shí)不應(yīng)該使用索引下一篇文章指南特殊的索引和集合固定集合索引全文本索引 上一篇文章:MongoDB指南---12、使用explain()和hint()、何時(shí)不應(yīng)該使用索引下一篇文章:MongoDB指南---14、特殊的索引和集合:固定集合...
摘要:操作符如何使用索引有一些查詢完全無(wú)法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時(shí)能夠使用索引,但是通常它并不知道要如何使用索引。索引對(duì)象和數(shù)組允許深入文檔內(nèi)部,對(duì)嵌套字段和數(shù)組建立索引。 上一篇文章:MongoDB指南---10、索引、復(fù)合索引 簡(jiǎn)介下一篇文章:MongoDB指南---12、使用explain()和hint()、何時(shí)不應(yīng)該使用索引 1、使用復(fù)合索引 在多...
摘要:操作符如何使用索引有一些查詢完全無(wú)法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時(shí)能夠使用索引,但是通常它并不知道要如何使用索引。索引對(duì)象和數(shù)組允許深入文檔內(nèi)部,對(duì)嵌套字段和數(shù)組建立索引。 上一篇文章:MongoDB指南---10、索引、復(fù)合索引 簡(jiǎn)介下一篇文章:MongoDB指南---12、使用explain()和hint()、何時(shí)不應(yīng)該使用索引 1、使用復(fù)合索引 在多...
閱讀 4259·2023-04-26 02:40
閱讀 2696·2023-04-26 02:31
閱讀 2784·2021-11-15 18:08
閱讀 597·2021-11-12 10:36
閱讀 1461·2021-09-30 09:57
閱讀 5251·2021-09-22 15:31
閱讀 2658·2019-08-30 14:17
閱讀 1309·2019-08-30 12:58