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