GaussDB(DWS)支持多種相容模式,為了相容目標資料庫,各模式之間或多或少存在一些行為差異。這裡分享一個mysql相容模式下的表達式函數因不同寫法引發的結果差異案例。 ...
主要是講下Mongodb的索引的查看、創建、刪除、類型說明,還有就是Explain執行計劃的解釋說明。
可以轉載,但請註明出處。
之前自己寫的SpringBoot整合MongoDB的聚合查詢操作,感興趣的可以點擊查閱。
https://www.cnblogs.com/zaoyu/p/springboot-mongodb.html
數組相關的操作
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" ] } }
執行看看有5.17萬條數據
-
單列索引: 在一個欄位上創建的索引。
-
複合索引:由多個欄位組合一起創建的索引。使用的時候要註意最左首碼原則,避免索引無法命中(失效)。
-
文本索引(全文索引)和空間索引(GEO索引),不常用,這裡不講,具體自行網上查詢。
2. 查看
語法:
db.Collection.getIndexs();
返回內容說明:
[ // 返回一個數組,內容是所有索引 { "v" : 2.0, // 索引版本,可忽略 "key" : { // 索引加在哪個欄位,以及排序 "_id" : 1.0 // _id 是欄位, 1.0是正序(升序), -1.0是倒序(倒序) }, "name" : "_id_" // 索引名,如果添加的時候沒有指定,Mongo會自動生成。 } ]
舉例:
可以看到返回一個name為 _id_的索引。 作用於 _id上, 升序排列。
語法:
db.collection.createIndex(keys, options) // 組合索引 db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } ) // 其中 <fieldN>是欄位名 <typeN>是排序 註意:3.0.0 版本之前創建索引方法為 db.collection.ensureIndex()。 5.0之後ensureIndex() 已被移除。
舉例:
// 創建單欄位索引 db.dailyTrip.createIndex({"car_type":1}); // 返回結果 { "numIndexesBefore" : 1.0, // 新建索引前的索引數 "numIndexesAfter" : 2.0, // 新建索引後的索引數 "createdCollectionAutomatically" : false, // 一般情況下,如果該集合存在,這裡就是返回false。如果指定的集合不存在,MongoDB 預設情況下會自動創建該集合,併在該集合上創建索引,此時返回true。 "ok" : 1.0 // 創建結果 1.0則是成功。返回0則說明報錯了。 } // 如果創建的欄位的索引已經存在,則返回如下 { "numIndexesBefore" : 2.0, "numIndexesAfter" : 2.0, "note" : "all indexes already exist", // 提示已經存在 "ok" : 1.0 } // 創建複合索引 db.dailyTrip.createIndex({"parent_type":1, "car_type":1}) // 也可以指定自定義索引名 db.dailyTrip.createIndex({"parent_type":1, "car_type":1}, { name: "inx_parantType_carType" }) // 這裡自己試驗 // 來自官方文檔 https://www.mongodb.com/docs/manual/reference/command/createIndexes/
創建完畢後,再次查看當前索引情況
4. 刪除
語法:
刪除指定集合中所有索引:
db.collection.dropIndexes();
刪除指定集合中的具體索引:
db.collection.dropIndex("索引名稱");
舉例:
// 刪除指定索引 db.dailyTrip.dropIndex("car_type_1"); // 返回結果 { "nIndexesWas" : 3.0, // 指示在刪除索引之前集合中存在的索引數量。 "ok" : 1.0 } // 刪除所有,自行嘗試。 註意, 主鍵_id的索引是不會也不能被刪除的。 db.collection.dropIndexes();
5. 修改
無法直接修改,如果需要調整索引欄位、名稱、組合索引的順序,需要先刪除原來索引,再新建索引。
6. 索引使用原則
-
選擇經常被查詢的欄位來創建索引。那些在查詢條件、排序和聚合操作中頻繁出現的欄位是最佳選擇。
-
選擇合適的、常用作查詢條件的欄位,綜合判斷是否可以使用複合索引,如果可以優先複合索引。 比如多數情況是按照欄位A、B、C或者A、B條件去查詢,那麼可以優先考慮基於欄位A、B、C創建複合索引。 使用複合索引來覆蓋多個查詢條件,以減少索引的數量。
-
儘量使用索引覆蓋,和MySQL中的索引覆蓋是類似的。 覆蓋索引是指索引包含了查詢所需的欄位,從而避免了查詢需要訪問實際文檔。這可以提高查詢性能,減少IO操作。 其實這裡和上面的複合索引是相關的。
-
其他的還有包括使用Explain去查看執行計劃、定期監控性能、刪除冗餘無用的索引之類的,不一而足。 上面是基於實際開發經驗得出的常用手段原則。
7. 索引失效的情況
-
查詢條件不滿足索引首碼: 例如,複合索引是
{ a: 1, b: 1 }
,而查詢條件只包含{ b: 1 }
,則該複合索引將無法被使用。 -
數據類型不匹配: 如果查詢條件中的數據類型與索引欄位的數據類型不匹配,索引可能無法被使用。例如,如果索引欄位是數字類型,而查詢條件中使用了字元串類型的值,索引將無法被使用。
// 類似Mysql中的隱式轉換會使索引失效。
-
索引選擇性低: 索引的選擇性是指索引中不同值的唯一性程度。如果索引的選擇性很低,即索引中的值幾乎都相同,那麼使用該索引可能不會帶來明顯的性能提升。
// 這裡其實是說索引列的值的區分度,如果重覆度過高,那麼使用索引的性能可能不如不用,索引底層優化器可能不選擇使用索引。假如欄位gender只有2個值,male和female,其中一半數據是male,另一半是female,此時用gender索引,還不如不用。
-
使用不支持的操作符: 某些查詢操作符可能無法使用索引。例如,正則表達式查詢、模糊查詢(如
$text
操作符)等可能無法充分利用索引。 -
數據量較小: 當集合中的數據量較小時,MongoDB 可能會選擇全表掃描而不使用索引,因為全表掃描可能更快。
// 類似mysql中,一般建議小於1000條就不加索引,因為索引有額外開銷。
-
索引過期或損壞: 如果索引過期或損壞,MongoDB 將無法使用該索引。
-
索引被禁用: 如果在查詢時禁用了索引,或者索引的存儲引擎不支持該查詢,索引將無法被使用。
-
索引尺寸過大: 如果索引的尺寸超過了 MongoDB 的限制,該索引可能無法被使用。
使用 explain()
方法可以查看查詢計劃和索引使用情況,幫助識別索引失效的原因,併進行相應的優化。
接下來對Explain方法做說明解釋。
二、執行計劃 Explain()
1.
-
explain()
是一個用於查詢解釋和性能分析的方法(函數)。 -
可以在
find()
、aggregate()
和count()
等查詢操作的結果上調用explain()
方法。 -
其作用:
-
有助於瞭解查詢的執行計劃、索引使用情況以及查詢性能的相關指標。
-
調用該方法後。MongoDB 會返回一條包含查詢執行計劃的文檔(結果)(具體說明如下),其中包含了查詢優化器的決策、索引使用情況、掃描文檔數量等信息。通過分析
explain()
返回的執行計劃,可以確定是否使用了適當的索引,是否存在潛在的性能問題,並根據需要進行索引優化、查詢重寫等操作,以提高查詢性能。
-
-
執行計劃的返回結果:
-
queryPlanner
:查詢優化器的決策和統計信息。
-
winningPlan
:優化器選擇的最佳執行計劃。
-
executionStats
:執行計劃的統計信息,如掃描的文檔數量、查詢時間等。
-
serverInfo
:MongoDB 伺服器的信息。
-
註意:explain()` 的輸出非常詳細,包含了大量的信息。因此,它在調試和優化查詢時非常有用,但在生產環境中不應該頻繁地使用,以避免對性能產生負面影響。
官方文檔:
-
explain的詳細說明 https://www.mongodb.com/docs/v5.0/reference/command/explain/
-
explain的返回結果的詳細說明 https://www.mongodb.com/docs/v5.0/reference/explain-results/
語法 // 普通的語句,直接在語句後面加上explain,也可以挪到find的前面。 db.collection.find({}).explain(<optional verbosity mode>); // 對聚合pipeline的執行計劃分析,explain放前面 db.collection.explain(<optional verbosity mode>).aggregate([]); <optional verbosity mode> 是可選的輸出模式。 如果什麼都不寫,比如 db.collection.find({"name"”":"onepiece"}).explain(); 那麼mongo會走預設模式 "queryPlanner"。 如果要指定模式,則直接加上,比如 db.collection.find({"name"”":"onepiece"}).explain("executionStats")
explain有三種模式:
-
queryPlanner (預設) : 只列出所有可能執行的方案,不會執行實際的語句,顯示已經勝出的方案winningPlan(最佳查詢計劃)。
-
executionStats : 只執行winningPlan方案,並輸出結果。
-
allPlansExecution :執行所有的方案,並輸出結果。
3. 三種模式下的說明和結果解釋
3.1 queryPlanner 模式
運行查詢優化器對當前的查詢進行評估並選擇一個最佳的查詢計劃,不執行實際語句。
下麵三種模式均以這個查詢語句為例:
db.getCollection("dailyTrip").explain().find({"parent_type":"Ride-hailing apps"}, { _id:0});
下麵是一個執行的返回結果,我把詳細內容先收起,可以看到返回的欄位有哪些。
具體解釋
{ "explainVersion" : "1", // 執行計劃的版本,可以忽略 "queryPlanner" : { // queryPlanner,就是執行計劃。 這部分重點看 "namespace" : "test.dailyTrip", // 就是collection和所在db "indexFilterSet" : false, // 是否設置了索引過濾器集合,Filter決定了查詢優化器對於某個查詢將如何使用索引 "parsedQuery" : { // 經過解析後的query內容。 "parent_type" : { "$eq" : "Ride-hailing apps" } }, "queryHash" : "8B8C334A", // 查詢的哈希值, "planCacheKey" : "F910A33F", // 查詢執行計劃的緩存鍵 "maxIndexedOrSolutionsReached" : false, // 是否已達到最大索引【或】解決方案的限制。 跳過 "maxIndexedAndSolutionsReached" : false, // 表示是否已達到最大索引【和】解決方案的限制。 跳過 "maxScansToExplodeReached" : false, // 是否已達到最大掃描數的限制。 跳過 "winningPlan" : { // 這個很關鍵,這裡是最後底層選擇的執行計劃。 "stage" : "PROJECTION_DEFAULT", // stage 是指步驟 可以由stage名稱看到具體做了什麼。 projection是應用投影操作,選擇所需的欄位 "transformBy" : { // 具體投影內容 跳過 "_id" : 0.0 }, "inputStage" : { // 執行階段的子階段,這裡是一個FETCH的子過程 "stage" : "FETCH", // FETCH是 從索引中獲取文檔數據。 "inputStage" : { // 其中一個階段,具體操作。 這裡面的東西是重點要看的。 "stage" : "IXSCAN", // 這裡是說使用索引進行掃描,通常表示優化的查詢。 "keyPattern" : { // 表示下麵的索引欄位的排列方式,1.0正序,-1.0倒序。 "parent_type" : 1.0, "car_type" : 1.0 }, "indexName" : "parent_type_1_car_type_1", // 用到的索引名稱 "isMultiKey" : false, // 是否為多鍵索引。 如果為 true,表示索引包含數組值 "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]" ] } } } }, "rejectedPlans" : [ // 底層優化器拒絕的計劃,沒有執行的計劃。 ] }, "command" : { // 語句具體涉及到的命令、collection、DB "find" : "dailyTrip", // 表名 從哪個collection中查找 "filter" : { // 哪個階段,這裡就是過濾 "parent_type" : "Ride-hailing apps" }, "projection" : { // 投影 控制要返回的欄位 "_id" : 0.0 }, "$db" : "test" // 庫 }, "serverInfo" : { // 伺服器信息 "host" : "onepiece-pc", // mongo的示例主機名稱 "port" : 27017.0, // 埠 "version" : "5.0.9", // mongodb的版本 "gitVersion" : "6f7dae919422dcd7*****************1ad00e6" // 這裡是git版本,這裡無關緊要,我脫敏了。 }, "serverParameters" : { // 伺服器的參數,各種緩存大小、最大閾值設置,暫時和我們這裡說的內容無關,跳過。 "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");
運行查詢優化器對當前的查詢進行評估並選擇一個最佳的查詢計划進行執行,在執行完畢後返回這個最佳執行計劃執行完成時的相關統計信息。
{ "explainVersion" : "1", "queryPlanner" : { // ... 同上,省略。 "parsedQuery" : { // ... 同上,省略。 }, // ... 同上,省略。 "winningPlan" : { // ... 同上,省略。 } "rejectedPlans" : [ // ... 同上,省略。 ] }, "executionStats" : { // 重點留意這裡面的內容 "executionSuccess" : true, // 執行結果 "nReturned" : 23010.0, // 返回的文檔數 "executionTimeMillis" : 70.0, // 執行耗時 ms "totalKeysExamined" : 23010.0, // 掃描了的索引總數 "totalDocsExamined" : 23010.0, // 總的掃描文檔數 "executionStages" : { // 執行stage 裡面會有具體每個stage的詳細 "stage" : "PROJECTION_DEFAULT", // 階段類型 "nReturned" : 23010.0, "executionTimeMillisEstimate" : 10.0, // 預估執行時間 ms "works" : 23011.0, // 階段中掃描任務數 "advanced" : 23010.0, // 階段中向上提交數量 "needTime" : 0.0, // 階段中定位索引位置所需次數 "needYield" : 0.0, // 階段中獲取鎖等待時間 "saveState" : 23.0, // :表示在查詢執行過程中,保存中間狀態所花費的時間 ms "restoreState" : 23.0, // 表示在查詢執行過程中,恢復之前保存的中間狀態所花費的時間 ms "isEOF" : 1.0, // 階段中是否到達流的結束位,對於limit限制符的查詢可能為0 "transformBy" : { // 表示查詢計劃是否使用了投影操作來轉換結果 "_id" : 0.0 }, "inputStage" : { // 執行階段的子階段,這裡是一個fetch的子過程 "stage" : "FETCH", // 內容差不多 "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模式
// 調用explain("allPlansExecution"), allPlansExecution 模式,執行所有的方案,並輸出結果。 db.getCollection("dailyTrip").find({"parent_type":"Ride-hailing apps"}, { _id:0}).explain("allPlansExecution");
allPlansExecution相比executionStats,其他的備選執行計劃也會去執行,並統計結果出來。 會存放在 executionStats中。
即按照最佳的執行計劃執行以及列出統計信息, 如果有多個查詢計劃,還會列出這些非最佳執行計劃部分的統計信息。
{ "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" : [ // 重點:allPlansExecution 如果有其他執行計劃,那麼會在這裡把執行統計結果放出來。 ] }, "command" : {// ... 同上,省略。 }, "serverInfo" : {// ... 同上,省略。 }, "serverParameters" : {// ... 同上,省略。 }, "ok" : 1.0 }
Stage參數值
-
COLLSCAN:全表掃描
-
IXSCAN:索引掃描
-
FETCH:根據索引去檢索指定document
-
SHARD_MERGE:將各個分片返回數據進行merge
-
SORT:表明在記憶體中進行了排序
-
LIMIT:使用limit限制返回數
-
SKIP:使用skip進行跳過
-
IDHACK:針對_id進行查詢
-
SHARDING_FILTER:通過mongos對分片數據進行查詢 ---伺服器是分片的才有。
-
COUNT:利用db.coll.explain().count()之類進行count運算
-
COUNTSCAN: count不使用Index進行count時的stage返回
-
COUNT_SCAN: count使用了Index進行count時的stage返回
-
SUBPLA:未使用到索引的$or查詢的stage返回
-
TEXT:使用全文索引進行查詢時候的stage返回
-
PROJECTION:限定返回欄位時候stage的返回
參考文檔
https://www.cnblogs.com/littleatp/p/8419678.html
4. 查詢優化思路
-
儘量使用索引。 舉例,比如通過上面的執行計劃發現某個作為查詢條件的欄位,沒有用上索引,且通過索引可以極大提高性能,那麼可以考慮對該欄位增加索引。
-
掃描文檔數越小越好。舉例,比如上面的執行計劃中,某個階段返回的掃描文檔數量極大,那麼可以考慮優化語句,比如調整語句順序,先過濾再處理;或者對查詢條件欄位增加索引。
完。
感謝查閱,希望對你有幫助,點個贊再走唄~