主要是講下Mongodb的索引的查看、創(chuàng)建、刪除、類型說明,還有就是Explain執(zhí)行計劃的解釋說明。?
?
可以轉(zhuǎn)載,但請注明出處。??
之前自己寫的SpringBoot整合MongoDB的聚合查詢操作,感興趣的可以點(diǎn)擊查閱。
https://www.cnblogs.com/zaoyu/p/springboot-mongodb.html
數(shù)組相關(guān)的操作
https://www.cnblogs.com/zaoyu/p/mongodb_array_operator.html
?
一、索引操作
{ "_id" : ObjectId("63ec5a971ddbe429cbeeffe3"), // object id "car_type" : "Gett", // string "date" : ISODate("2016-04-01T00:00:00.000+0000"), //ISODate "trips" : 0.0, // number "monthly" : "NA", // string "parent_type" : "Ride-hailing apps", // string "monthly_is_estimated" : true, // boolean "geo" : { // object "$concat" : [ // array "$parent_type", "$grouping" ] } }
執(zhí)行看看有5.17萬條數(shù)據(jù)
1. 索引類型
-
單列索引: 在一個字段上創(chuàng)建的索引。
-
復(fù)合索引:由多個字段組合一起創(chuàng)建的索引。使用的時候要注意最左前綴原則,避免索引無法命中(失效)。
-
文本索引(全文索引)和空間索引(GEO索引),不常用,這里不講,具體自行網(wǎng)上查詢。
?
2. 查看
語法:
db.Collection.getIndexs();
返回內(nèi)容說明:
[ // 返回一個數(shù)組,內(nèi)容是所有索引 { "v" : 2.0, // 索引版本,可忽略 "key" : { // 索引加在哪個字段,以及排序 "_id" : 1.0 // _id 是字段, 1.0是正序(升序), -1.0是倒序(倒序) }, "name" : "_id_" // 索引名,如果添加的時候沒有指定,Mongo會自動生成。 } ]
舉例:
可以看到返回一個name為 _id_的索引。 作用于 _id上, 升序排列。
3. 創(chuàng)建
語法:
db.collection.createIndex(keys, options) // 組合索引 db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } ) // 其中 <fieldN>是字段名 <typeN>是排序 注意:3.0.0 版本之前創(chuàng)建索引方法為 db.collection.ensureIndex()。 5.0之后ensureIndex() 已被移除。
舉例:
// 創(chuàng)建單字段索引 db.dailyTrip.createIndex({"car_type":1}); // 返回結(jié)果 { "numIndexesBefore" : 1.0, // 新建索引前的索引數(shù) "numIndexesAfter" : 2.0, // 新建索引后的索引數(shù) "createdCollectionAutomatically" : false, // 一般情況下,如果該集合存在,這里就是返回false。如果指定的集合不存在,MongoDB 默認(rèn)情況下會自動創(chuàng)建該集合,并在該集合上創(chuàng)建索引,此時返回true。 "ok" : 1.0 // 創(chuàng)建結(jié)果 1.0則是成功。返回0則說明報錯了。 } // 如果創(chuàng)建的字段的索引已經(jīng)存在,則返回如下 { "numIndexesBefore" : 2.0, "numIndexesAfter" : 2.0, "note" : "all indexes already exist", // 提示已經(jīng)存在 "ok" : 1.0 } // 創(chuàng)建復(fù)合索引 db.dailyTrip.createIndex({"parent_type":1, "car_type":1}) // 也可以指定自定義索引名 db.dailyTrip.createIndex({"parent_type":1, "car_type":1}, { name: "inx_parantType_carType" }) // 這里自己試驗(yàn) // 來自官方文檔 https://www.mongodb.com/docs/manual/reference/command/createIndexes/
?
?創(chuàng)建完畢后,再次查看當(dāng)前索引情況
?
4. 刪除
語法:
刪除指定集合中所有索引:
db.collection.dropIndexes();
刪除指定集合中的具體索引:
db.collection.dropIndex("索引名稱");
舉例:
// 刪除指定索引 db.dailyTrip.dropIndex("car_type_1"); // 返回結(jié)果 { "nIndexesWas" : 3.0, // 指示在刪除索引之前集合中存在的索引數(shù)量。 "ok" : 1.0 } // 刪除所有,自行嘗試。 注意, 主鍵_id的索引是不會也不能被刪除的。 db.collection.dropIndexes();
?
5. 修改
無法直接修改,如果需要調(diào)整索引字段、名稱、組合索引的順序,需要先刪除原來索引,再新建索引。
6. 索引使用原則
-
選擇經(jīng)常被查詢的字段來創(chuàng)建索引。那些在查詢條件、排序和聚合操作中頻繁出現(xiàn)的字段是最佳選擇。
-
選擇合適的、常用作查詢條件的字段,綜合判斷是否可以使用復(fù)合索引,如果可以優(yōu)先復(fù)合索引。 比如多數(shù)情況是按照字段A、B、C或者A、B條件去查詢,那么可以優(yōu)先考慮基于字段A、B、C創(chuàng)建復(fù)合索引。 使用復(fù)合索引來覆蓋多個查詢條件,以減少索引的數(shù)量。
-
盡量使用索引覆蓋,和MySQL中的索引覆蓋是類似的。 覆蓋索引是指索引包含了查詢所需的字段,從而避免了查詢需要訪問實(shí)際文檔。這可以提高查詢性能,減少IO操作。 其實(shí)這里和上面的復(fù)合索引是相關(guān)的。
-
其他的還有包括使用Explain去查看執(zhí)行計劃、定期監(jiān)控性能、刪除冗余無用的索引之類的,不一而足。 上面是基于實(shí)際開發(fā)經(jīng)驗(yàn)得出的常用手段原則。
?
7. 索引失效的情況
-
查詢條件不滿足索引前綴: 例如,復(fù)合索引是
{ a: 1, b: 1 }
,而查詢條件只包含{ b: 1 }
,則該復(fù)合索引將無法被使用。 -
數(shù)據(jù)類型不匹配: 如果查詢條件中的數(shù)據(jù)類型與索引字段的數(shù)據(jù)類型不匹配,索引可能無法被使用。例如,如果索引字段是數(shù)字類型,而查詢條件中使用了字符串類型的值,索引將無法被使用。
// 類似Mysql中的隱式轉(zhuǎn)換會使索引失效。
-
索引選擇性低: 索引的選擇性是指索引中不同值的唯一性程度。如果索引的選擇性很低,即索引中的值幾乎都相同,那么使用該索引可能不會帶來明顯的性能提升。
// 這里其實(shí)是說索引列的值的區(qū)分度,如果重復(fù)度過高,那么使用索引的性能可能不如不用,索引底層優(yōu)化器可能不選擇使用索引。假如字段gender只有2個值,male和female,其中一半數(shù)據(jù)是male,另一半是female,此時用gender索引,還不如不用。
-
使用不支持的操作符: 某些查詢操作符可能無法使用索引。例如,正則表達(dá)式查詢、模糊查詢(如
$text
操作符)等可能無法充分利用索引。 -
數(shù)據(jù)量較?。?/strong> 當(dāng)集合中的數(shù)據(jù)量較小時,MongoDB 可能會選擇全表掃描而不使用索引,因?yàn)槿頀呙杩赡芨臁?/span>
// 類似mysql中,一般建議小于1000條就不加索引,因?yàn)樗饕蓄~外開銷。
-
索引過期或損壞: 如果索引過期或損壞,MongoDB 將無法使用該索引。
-
索引被禁用: 如果在查詢時禁用了索引,或者索引的存儲引擎不支持該查詢,索引將無法被使用。
-
索引尺寸過大: 如果索引的尺寸超過了 MongoDB 的限制,該索引可能無法被使用。
使用 explain()
方法可以查看查詢計劃和索引使用情況,幫助識別索引失效的原因,并進(jìn)行相應(yīng)的優(yōu)化。
接下來對Explain方法做說明解釋。
?
?
二、執(zhí)行計劃 Explain()
1. 什么是Explain()執(zhí)行計劃?
-
explain()
是一個用于查詢解釋和性能分析的方法(函數(shù))。 -
可以在
find()
、aggregate()
和count()
等查詢操作的結(jié)果上調(diào)用explain()
方法。 -
其作用:
-
有助于了解查詢的執(zhí)行計劃、索引使用情況以及查詢性能的相關(guān)指標(biāo)。
-
調(diào)用該方法后。MongoDB 會返回一條包含查詢執(zhí)行計劃的文檔(結(jié)果)(具體說明如下),其中包含了查詢優(yōu)化器的決策、索引使用情況、掃描文檔數(shù)量等信息。通過分析
explain()
返回的執(zhí)行計劃,可以確定是否使用了適當(dāng)?shù)乃饕?,是否存在潛在的性能問題,并根據(jù)需要進(jìn)行索引優(yōu)化、查詢重寫等操作,以提高查詢性能。
-
-
執(zhí)行計劃的返回結(jié)果:
-
queryPlanner
:查詢優(yōu)化器的決策和統(tǒng)計信息。
-
winningPlan
:優(yōu)化器選擇的最佳執(zhí)行計劃。
-
executionStats
:執(zhí)行計劃的統(tǒng)計信息,如掃描的文檔數(shù)量、查詢時間等。
-
serverInfo
:MongoDB 服務(wù)器的信息。
-
注意:explain()` 的輸出非常詳細(xì),包含了大量的信息。因此,它在調(diào)試和優(yōu)化查詢時非常有用,但在生產(chǎn)環(huán)境中不應(yīng)該頻繁地使用,以避免對性能產(chǎn)生負(fù)面影響。
官方文檔:
-
explain的詳細(xì)說明 https://www.mongodb.com/docs/v5.0/reference/command/explain/
-
explain的返回結(jié)果的詳細(xì)說明 https://www.mongodb.com/docs/v5.0/reference/explain-results/
2. Explain的語法和使用
語法 // 普通的語句,直接在語句后面加上explain,也可以挪到find的前面。 db.collection.find({}).explain(<optional verbosity mode>); // 對聚合pipeline的執(zhí)行計劃分析,explain放前面 db.collection.explain(<optional verbosity mode>).aggregate([]); <optional verbosity mode> 是可選的輸出模式。 如果什么都不寫,比如 db.collection.find({"name"”":"onepiece"}).explain(); 那么mongo會走默認(rèn)模式 "queryPlanner"。 如果要指定模式,則直接加上,比如 db.collection.find({"name"”":"onepiece"}).explain("executionStats")
explain有三種模式:
-
queryPlanner (默認(rèn)) : 只列出所有可能執(zhí)行的方案,不會執(zhí)行實(shí)際的語句,顯示已經(jīng)勝出的方案winningPlan(最佳查詢計劃)。
-
executionStats : 只執(zhí)行winningPlan方案,并輸出結(jié)果。
-
allPlansExecution :執(zhí)行所有的方案,并輸出結(jié)果。
?
3. 三種模式下的說明和結(jié)果解釋
3.1 queryPlanner 模式
運(yùn)行查詢優(yōu)化器對當(dāng)前的查詢進(jìn)行評估并選擇一個最佳的查詢計劃,不執(zhí)行實(shí)際語句。
?
下面三種模式均以這個查詢語句為例:
db.getCollection("dailyTrip").explain().find({"parent_type":"Ride-hailing apps"}, { _id:0});
下面是一個執(zhí)行的返回結(jié)果,我把詳細(xì)內(nèi)容先收起,可以看到返回的字段有哪些。
?
具體解釋
{ "explainVersion" : "1", // 執(zhí)行計劃的版本,可以忽略 "queryPlanner" : { // queryPlanner,就是執(zhí)行計劃。 這部分重點(diǎn)看 "namespace" : "test.dailyTrip", // 就是collection和所在db "indexFilterSet" : false, // 是否設(shè)置了索引過濾器集合,F(xiàn)ilter決定了查詢優(yōu)化器對于某個查詢將如何使用索引 "parsedQuery" : { // 經(jīng)過解析后的query內(nèi)容。 "parent_type" : { "$eq" : "Ride-hailing apps" } }, "queryHash" : "8B8C334A", // 查詢的哈希值, "planCacheKey" : "F910A33F", // 查詢執(zhí)行計劃的緩存鍵 "maxIndexedOrSolutionsReached" : false, // 是否已達(dá)到最大索引【或】解決方案的限制。 跳過 "maxIndexedAndSolutionsReached" : false, // 表示是否已達(dá)到最大索引【和】解決方案的限制。 跳過 "maxScansToExplodeReached" : false, // 是否已達(dá)到最大掃描數(shù)的限制。 跳過 "winningPlan" : { // 這個很關(guān)鍵,這里是最后底層選擇的執(zhí)行計劃。 "stage" : "PROJECTION_DEFAULT", // stage 是指步驟 可以由stage名稱看到具體做了什么。 projection是應(yīng)用投影操作,選擇所需的字段 "transformBy" : { // 具體投影內(nèi)容 跳過 "_id" : 0.0 }, "inputStage" : { // 執(zhí)行階段的子階段,這里是一個FETCH的子過程 "stage" : "FETCH", // FETCH是 從索引中獲取文檔數(shù)據(jù)。 "inputStage" : { // 其中一個階段,具體操作。 這里面的東西是重點(diǎn)要看的。 "stage" : "IXSCAN", // 這里是說使用索引進(jìn)行掃描,通常表示優(yōu)化的查詢。 "keyPattern" : { // 表示下面的索引字段的排列方式,1.0正序,-1.0倒序。 "parent_type" : 1.0, "car_type" : 1.0 }, "indexName" : "parent_type_1_car_type_1", // 用到的索引名稱 "isMultiKey" : false, // 是否為多鍵索引。 如果為 true,表示索引包含數(shù)組值 "multiKeyPaths" : { // 如果索引是多鍵索引,這個屬性將會包含索引中包含數(shù)組值的字段路徑。本例中的索引不是多鍵索引,因此下面的字段為空數(shù)組。 "parent_type" : [ ], "car_type" : [ ] }, "isUnique" : false, // 是否為唯一索引 "isSparse" : false, // 是否為稀疏索引 "isPartial" : false, // 是否為部分索引 "indexVersion" : 2.0, // 索引版本 "direction" : "forward", // 索引的遍歷方向 "indexBounds" : { // 當(dāng)前查詢具體使用的索引 "parent_type" : [ "[\"Ride-hailing apps\", \"Ride-hailing apps\"]" ], "car_type" : [ "[MinKey, MaxKey]" ] } } } }, "rejectedPlans" : [ // 底層優(yōu)化器拒絕的計劃,沒有執(zhí)行的計劃。 ] }, "command" : { // 語句具體涉及到的命令、collection、DB "find" : "dailyTrip", // 表名 從哪個collection中查找 "filter" : { // 哪個階段,這里就是過濾 "parent_type" : "Ride-hailing apps" }, "projection" : { // 投影 控制要返回的字段 "_id" : 0.0 }, "$db" : "test" // 庫 }, "serverInfo" : { // 服務(wù)器信息 "host" : "onepiece-pc", // mongo的示例主機(jī)名稱 "port" : 27017.0, // 端口 "version" : "5.0.9", // mongodb的版本 "gitVersion" : "6f7dae919422dcd7*****************1ad00e6" // 這里是git版本,這里無關(guān)緊要,我脫敏了。 }, "serverParameters" : { // 服務(wù)器的參數(shù),各種緩存大小、最大閾值設(shè)置,暫時和我們這里說的內(nèi)容無關(guān),跳過。 "internalQueryFacetBufferSizeBytes" : 104857600.0, "internalQueryFacetMaxOutputDocSizeBytes" : 104857600.0, "internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600.0, "internalDocumentSourceGroupMaxMemoryBytes" : 104857600.0, "internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600.0, "internalQueryProhibitBlockingMergeOnMongoS" : 0.0, "internalQueryMaxAddToSetBytes" : 104857600.0, "internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600.0 }, "ok" : 1.0 }
3.2 executionStats模式
db.getCollection("dailyTrip").find({"parent_type":"Ride-hailing apps"}, { _id:0}).explain("executionStats");
運(yùn)行查詢優(yōu)化器對當(dāng)前的查詢進(jìn)行評估并選擇一個最佳的查詢計劃進(jìn)行執(zhí)行,在執(zhí)行完畢后返回這個最佳執(zhí)行計劃執(zhí)行完成時的相關(guān)統(tǒng)計信息。
?
{ "explainVersion" : "1", "queryPlanner" : { // ... 同上,省略。 "parsedQuery" : { // ... 同上,省略。 }, // ... 同上,省略。 "winningPlan" : { // ... 同上,省略。 } "rejectedPlans" : [ // ... 同上,省略。 ] }, "executionStats" : { // 重點(diǎn)留意這里面的內(nèi)容 "executionSuccess" : true, // 執(zhí)行結(jié)果 "nReturned" : 23010.0, // 返回的文檔數(shù) "executionTimeMillis" : 70.0, // 執(zhí)行耗時 ms "totalKeysExamined" : 23010.0, // 掃描了的索引總數(shù) "totalDocsExamined" : 23010.0, // 總的掃描文檔數(shù) "executionStages" : { // 執(zhí)行stage 里面會有具體每個stage的詳細(xì) "stage" : "PROJECTION_DEFAULT", // 階段類型 "nReturned" : 23010.0, "executionTimeMillisEstimate" : 10.0, // 預(yù)估執(zhí)行時間 ms "works" : 23011.0, // 階段中掃描任務(wù)數(shù) "advanced" : 23010.0, // 階段中向上提交數(shù)量 "needTime" : 0.0, // 階段中定位索引位置所需次數(shù) "needYield" : 0.0, // 階段中獲取鎖等待時間 "saveState" : 23.0, // :表示在查詢執(zhí)行過程中,保存中間狀態(tài)所花費(fèi)的時間 ms "restoreState" : 23.0, // 表示在查詢執(zhí)行過程中,恢復(fù)之前保存的中間狀態(tài)所花費(fèi)的時間 ms "isEOF" : 1.0, // 階段中是否到達(dá)流的結(jié)束位,對于limit限制符的查詢可能為0 "transformBy" : { // 表示查詢計劃是否使用了投影操作來轉(zhuǎn)換結(jié)果 "_id" : 0.0 }, "inputStage" : { // 執(zhí)行階段的子階段,這里是一個fetch的子過程 "stage" : "FETCH", // 內(nèi)容差不多 "nReturned" : 23010.0, "executionTimeMillisEstimate" : 4.0, "works" : 23011.0, "advanced" : 23010.0, "needTime" : 0.0, "needYield" : 0.0, "saveState" : 23.0, "restoreState" : 23.0, "isEOF" : 1.0, "docsExamined" : 23010.0, "alreadyHasObj" : 0.0, "inputStage" : { // 子階段,ixscan子過程 "stage" : "IXSCAN", "nReturned" : 23010.0, "executionTimeMillisEstimate" : 4.0, "works" : 23011.0, "advanced" : 23010.0, "needTime" : 0.0, "needYield" : 0.0, "saveState" : 23.0, "restoreState" : 23.0, "isEOF" : 1.0, "keyPattern" : { "parent_type" : 1.0, "car_type" : 1.0 }, "indexName" : "parent_type_1_car_type_1", "isMultiKey" : false, "multiKeyPaths" : { "parent_type" : [ ], "car_type" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2.0, "direction" : "forward", "indexBounds" : { "parent_type" : [ "[\"Ride-hailing apps\", \"Ride-hailing apps\"]" ], "car_type" : [ "[MinKey, MaxKey]" ] }, "keysExamined" : 23010.0, "seeks" : 1.0, "dupsTested" : 0.0, "dupsDropped" : 0.0 } } } }, "command" : { // ... 同上,省略。 }, "serverInfo" : { // ... 同上,省略。 }, "serverParameters" : {// ... 同上,省略。 }, "ok" : 1.0 }
3.3 allPlansExecution模式
// 調(diào)用explain("allPlansExecution"), allPlansExecution 模式,執(zhí)行所有的方案,并輸出結(jié)果。 db.getCollection("dailyTrip").find({"parent_type":"Ride-hailing apps"}, { _id:0}).explain("allPlansExecution");
allPlansExecution相比executionStats,其他的備選執(zhí)行計劃也會去執(zhí)行,并統(tǒng)計結(jié)果出來。 會存放在 executionStats中。
即按照最佳的執(zhí)行計劃執(zhí)行以及列出統(tǒng)計信息, 如果有多個查詢計劃,還會列出這些非最佳執(zhí)行計劃部分的統(tǒng)計信息。
?
{ "explainVersion" : "1", "queryPlanner" : { "namespace" : "test.dailyTrip", "indexFilterSet" : false, "parsedQuery" : { "parent_type" : { "$eq" : "Ride-hailing apps" } }, "maxIndexedOrSolutionsReached" : false, "maxIndexedAndSolutionsReached" : false, "maxScansToExplodeReached" : false, "winningPlan" : { // ... 同上,省略。 }, "rejectedPlans" : [ // ... 同上,省略。 ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 23010.0, "executionTimeMillis" : 68.0, "totalKeysExamined" : 23010.0, "totalDocsExamined" : 23010.0, "executionStages" : { // 這里的executionStage和2.2的executionStats中的executionstages一樣,參考那里。 }, "allPlansExecution" : [ // 重點(diǎn):allPlansExecution 如果有其他執(zhí)行計劃,那么會在這里把執(zhí)行統(tǒng)計結(jié)果放出來。 ] }, "command" : {// ... 同上,省略。 }, "serverInfo" : {// ... 同上,省略。 }, "serverParameters" : {// ... 同上,省略。 }, "ok" : 1.0 }
Stage參數(shù)值
-
COLLSCAN:全表掃描
-
IXSCAN:索引掃描
-
FETCH:根據(jù)索引去檢索指定document
-
SHARD_MERGE:將各個分片返回數(shù)據(jù)進(jìn)行merge
-
SORT:表明在內(nèi)存中進(jìn)行了排序
-
LIMIT:使用limit限制返回數(shù)
-
SKIP:使用skip進(jìn)行跳過
-
IDHACK:針對_id進(jìn)行查詢
-
SHARDING_FILTER:通過mongos對分片數(shù)據(jù)進(jìn)行查詢 ---服務(wù)器是分片的才有。
-
COUNT:利用db.coll.explain().count()之類進(jìn)行count運(yùn)算
-
COUNTSCAN: count不使用Index進(jìn)行count時的stage返回
-
COUNT_SCAN: count使用了Index進(jìn)行count時的stage返回
-
SUBPLA:未使用到索引的$or查詢的stage返回
-
TEXT:使用全文索引進(jìn)行查詢時候的stage返回
-
PROJECTION:限定返回字段時候stage的返回
參考文檔
https://www.cnblogs.com/littleatp/p/8419678.html
?
4. 查詢優(yōu)化思路
-
盡量使用索引。 舉例,比如通過上面的執(zhí)行計劃發(fā)現(xiàn)某個作為查詢條件的字段,沒有用上索引,且通過索引可以極大提高性能,那么可以考慮對該字段增加索引。
-
掃描文檔數(shù)越小越好。舉例,比如上面的執(zhí)行計劃中,某個階段返回的掃描文檔數(shù)量極大,那么可以考慮優(yōu)化語句,比如調(diào)整語句順序,先過濾再處理;或者對查詢條件字段增加索引。
?
完。文章來源:http://www.zghlxwxcb.cn/news/detail-640346.html
感謝查閱,希望對你有幫助,點(diǎn)個贊再走唄~文章來源地址http://www.zghlxwxcb.cn/news/detail-640346.html
到了這里,關(guān)于MongoDB索引操作和執(zhí)行計劃Explain()詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!