MongoDB索引優化

- 作者: 博學谷狂野架構師
- GitHub:GitHub地址 (有我精心準備的130本電子書PDF)
只分享干貨、不吹水,讓我們一起加油!??
索引簡介
索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取資料時必須掃描集合中的每個檔案并選取那些符合查詢條件的記錄,
什么是索引
索引最常用的比喻就是書籍的目錄,查詢索引就像查詢一本書的目錄,本質上目錄是將書中一小部分內容資訊(比如題目)和內容的位置資訊(頁碼)共同構成,而由于資訊量小(只有題目),所以我們可以很快找到我們想要的資訊片段,再根據頁碼找到相應的內容,同樣索引也是只保留某個域的一部分資訊(建立了索引的field的資訊),以及對應的檔案的位置資訊,
假設我們有如下檔案(每行的資料在MongoDB中是存在于一個Document當中)
| 姓名 | id | 部門 | city | score |
|---|---|---|---|---|
| 張三 | 2 | xxx | Beijing | 90 |
| 李四 | 1 | xxx | Shanghai | 70 |
| 王五 | 3 | xxx | guangzhou | 60 |
索引的作用
假如我們想找id為2的document(即張三的記錄),如果沒有索引,我們就需要掃描整個資料表,然后找出所有為2的document,當資料表中有大量documents的時候,這個時間就會非常長(從磁盤上查找資料還涉及大量的IO操作),建立索引后會有什么變化呢?MongoDB會將id資料拿出來建立索引資料,如下
| 索引值 | 位置 |
|---|---|
| 1 | pos2 |
| 2 | pos1 |
| 3 | pos3 |
索引的作業原理
這樣我們就可以通過掃描這個小表找到document對應的位置,
查找程序示意圖如下:

索引為什么這么快
為什么這樣速度會快呢?這主要有幾方面的因素
- 索引資料通過B樹來存盤,從而使得搜索的時間復雜度為O(logdN)級別的(d是B樹的度, 通常d的值比較大,比如大于100),比原先O(N)的復雜度大幅下降,這個差距是驚人的,以一個實際例子來看,假設d=100,N=1億,那么O(logdN) = 8, 而O(N)是1億,是的,這就是演算法的威力,
- 索引本身是在高速快取當中,相比磁盤IO操作會有大幅的性能提升,(需要注意的是,有的時候資料量非常大的時候,索引資料也會非常大,當大到超出記憶體容量的時候,會導致部分索引資料存盤在磁盤上,這會導致磁盤IO的開銷大幅增加,從而影響性能,所以務必要保證有足夠的記憶體能容下所有的索引資料)
當然,事物總有其兩面性,在提升查詢速度的同時,由于要建立索引,所以寫入操作時就需要額外的添加索引的操作,這必然會影響寫入的性能,所以當有大量寫操作而讀操作比較少的時候,且對讀操作性能不需要考慮的時候,就不適合建立索引,當然,目前大多數互聯網應用都是讀操作遠大于寫操作,因此建立索引很多時候是非常劃算和必要的操作,
查看索引
索引是提高查詢查詢效率最有效的手段,索引是一種特殊的資料結構,索引以易于遍歷的形式存盤了資料的部分內容(如:一個特定的欄位或一組欄位值),索引會按一定規則對存盤值進行排序,而且索引的存盤位置在記憶體中,所在從索引中檢索資料會非常快,如果沒有索引,MongoDB必須掃描集合中的每一個檔案,這種掃描的效率非常低,尤其是在資料量較大時,
默認主鍵索引
在創建集合期間,MongoDB 在_id]欄位上 創建唯一索引,該索引可防止客戶端插入兩個具有相同值的檔案,您不能將此索引放在欄位上,
查看索引
查看集合索引
要回傳集合中所有索引的串列可以使用
db.collection.getIndexes()查看現有索引
COPYdb.zips.getIndexes();
查看
zips集合的所有索引,我們看到有一個默認的_id_索引,并且是一個升序索引

查看資料庫
若要列出資料庫中所有集合的所有索引,則需在 MongoDB 的 Shell 客戶端中進行以下操作:
COPYdb.getCollectionNames().forEach(function(collection){
indexes = db[collection].getIndexes();
print("Indexes for [" + collection + "]:" );
printjson(indexes);
});
這樣可以列出本資料庫的所有集合的索引

索引常用操作
創建索引
MongoDB使用 createIndex() 方法來創建索引,
注意在 3.0.0 版本前創建索引方法為 db.collection.ensureIndex(),之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 還能用,但只是 createIndex() 的別名,
語法
createIndex()方法基本語法格式如下所示:
COPYdb.collection.createIndex(keys, options)
語法中 Key 值為你要創建的索引欄位,1 為指定按升序創建索引,如果你想按降序來創建索引指定為 -1 即可,
COPYdb.zips.createIndex({"pop":1})
這樣就根據
pop欄位創建了一個升序索引

索引引數
createIndex() 接收可選引數,可選引數串列如下
| Parameter | Type | Description |
|---|---|---|
| background | Boolean | 建索引程序會阻塞其它資料庫操作,background可指定以后臺方式創建索引,即增加 “background” 可選引數, “background” 默認值為false, |
| unique | Boolean | 建立的索引是否唯一,指定為true創建唯一索引,默認值為false. |
| name | string | 索引的名稱,如果未指定,MongoDB的通過連接索引的欄位名和排序順序生成一個索引名稱, |
| dropDups | Boolean | 3.0+版本已廢棄,在建立唯一索引時是否洗掉重復記錄,指定 true 創建唯一索引,默認值為 false. |
| sparse | Boolean | 對檔案中不存在的欄位資料不啟用索引;這個引數需要特別注意,如果設定為true的話,在索引欄位中不會查詢出不包含對應欄位的檔案.,默認值為 false. |
| expireAfterSeconds | integer | 指定一個以秒為單位的數值,完成 TTL設定,設定集合的生存時間, |
| v | index version | 索引的版本號,默認的索引版本取決于mongod創建索引時運行的版本, |
| weights | document | 索引權重值,數值在 1 到 99,999 之間,表示該索引相對于其他索引欄位的得分權重, |
| default_language | string | 對于文本索引,該引數決定了停用詞及詞干和詞器的規則的串列, 默認為英語 |
| language_override | string | 對于文本索引,該引數指定了包含在檔案中的欄位名,語言覆寫默認的language,默認值為 language. |
示例
創建一個名稱是
pop_union_index的索引,按照pop欄位降序,并且在10秒后洗掉
COPYdb.zips.createIndex(
{
"pop":-1
},
{
"name":"pop_union_index",
"expireAfterSeconds":10
}
)
這樣我們就創建了一個索引

洗掉索引
MongoDB 提供的兩種從集合中洗掉索引的方法如下:
根據name洗掉
可以根據索引的名字進行索引洗掉
COPYdb.zips.dropIndex("loc_2d")
這樣我們就把一個索引洗掉了

根據欄位洗掉
還可以根據欄位進行洗掉
COPYdb.zips.dropIndex ({ "pop" : 1 })
洗掉集合中
pop欄位升序的索引,這樣就把這個索引洗掉了

洗掉所有索引
db.collection.dropIndexes()可以把集合所有索引洗掉
COPYdb.zips.dropIndexes()`
這樣就把非默認的主鍵索引意外的索引索引洗掉了

MongoDB索引型別
單鍵索引
MongoDB為檔案集合中任何欄位上的索引提供了完整的支持 ,默認情況下,所有集合在
_id欄位上都有一個索引,應用程式和用戶可以添加其他索引來支持重要的查詢和操作,

這個是最簡單最常用的索引型別,比如我們上邊的例子,為id建立一個單獨的索引就是此種型別,
創建索引
我們創建一個
pop人數升序的索引
COPYdb.zips.createIndex({'pop': 1})
其中
{'pop': 1}中的1表示升序,如果想設定倒序索引的話使用{'pop': -1}可

查看執行計劃
可以在查詢中使用執行計劃查看索引是否生效
COPYdb.zips.find({"pop":{$gt:10000}}).explain();
我們發現索引已經生效了

復合索引
復合索引(Compound Indexes)指一個索引包含多個欄位,用法和單鍵索引基本一致,使用復合索引時要注意欄位的順序,如下添加一個name和age的復合索引,name正序,age倒序,document首先按照name正序排序,然后name相同的document按age進行倒序排序,mongoDB中一個復合索引最多可以包含32個欄位,符合索引的原理如下圖所示:

上圖查詢索引的時候會先查詢userid,再查詢score,然后就可以找到對應的檔案,
創建索引
我們創建一個以
CUSHMAN升序,state降序的符合索引
COPYdb.zips.createIndex({"city": 1,"state":-1})
這樣我們就把索引創建了

查看執行計劃
COPYdb.zips.find({"city":"CUSHMAN","state":"NY"}).explain();
我們看到我們的查詢走了索引

對于復合索引需要注意以下幾點:
最左前綴法則
在MySQL中走前綴法則生效,在mongodb中查詢同樣生效
COPYdb.zips.find({"city":"CUSHMAN"}).explain();
我們只查詢最走側索引列的時候,索引是生效的

但是如果我們查詢不加入最左側索引列
COPYdb.zips.find({"state":"NY"}).explain();
我們發現索引未生效,走了全表掃描

地理索引
地理索引包含兩種地理型別,如果需要計算的地理資料表示為類似于地球的球形表面上的坐標,則可以使用 2dsphere 索引,
通常可以按照坐標軸、經度、緯度的方式把位置資料存盤為 GeoJSON 物件,GeoJSON 的坐標參考系使用的是 wgs84 資料,如果需要計算距離(在一個歐幾里得平面上),通常可以按照正常坐標對的形式存盤位置資料,可使用 2d 索引,
創建平面地理索引
如果查找的地方是小范圍的可以使用平面索引
COPYdb.zips.createIndex({"loc":"2d"})

創建球面地理索引
如果是大范圍的,需要考慮地球弧度的情況下如果使用平面坐標可能不準確,就需要使用球面索引
COPYdb.zips.createIndex({"loc":"2dsphere"})

常用索引屬性
唯一索引
唯一索引(unique indexes)用于為collection添加唯一約束,即強制要求collection中的索引欄位沒有重復值,添加唯一索引的語法:
COPYdb.zips.createIndex({"_id":1,"city":1},{unique:true,name:"id_union_index"})
這樣我們就創建了一個根據ID以及city的唯一索引

區域索引
區域索引(Partial Indexes)顧名思義,只對collection的一部分添加索引,創建索引的時候,根據過濾條件判斷是否對document添加索引,對于沒有添加索引的檔案查找時采用的全表掃描,對添加了索引的檔案查找時使用索引,
創建索引
COPYdb.zips.createIndex(
{
pop:1
},
{
partialFilterExpression:
{
pop:
{
$gt: 10000
}
}
}
)
這樣就創建了區域索引

查看執行計劃
根據索引特性 ,我們知道,只有查找的人數大于10000,才會走索引
COPYdb.zips.find({"pop":9999}).explain()
我們看到,查詢10000以內的資料不走索引

如果查找的條件大于10000就會走索引
COPYdb.zips.find({"pop":99999}).explain()

執行計劃
MongoDB中的
explain()函式可以幫助我們查看查詢相關的資訊,這有助于我們快速查找到搜索瓶頸進而解決它,本文我們就來看看explain()的一些用法及其查詢結果的含義,整體來說,
explain()的用法和sort()、limit()用法差不多,不同的是explain()必須放在最后面,
基本用法
先來看一個基本用法:
COPYdb.zips.find({"pop":99999}).explain()
直接跟在
find()函式后面,表示查看find()函式的執行計劃,結果如下:
COPY{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "zips-db.zips",
"indexFilterSet" : false,
"parsedQuery" : {
"pop" : {
"$eq" : 99999
}
},
"queryHash" : "891A44E4",
"planCacheKey" : "2D13A19E",
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"pop" : 1
},
"indexName" : "pop_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"pop" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : true,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"pop" : [
"[99999.0, 99999.0]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "localhost",
"port" : 27017,
"version" : "4.4.5",
"gitVersion" : "ff5cb77101b052fa02da43b8538093486cf9b3f7"
},
"ok" : 1
}
回傳結果包含兩大塊資訊,一個是 queryPlanner,即查詢計劃,還有一個是 serverInfo,即MongoDB服務的一些資訊,
引數解釋
那么這里涉及到的引數比較多,我們來一一看一下:
| 引數 | 含義 |
|---|---|
| plannerVersion | 查詢計劃版本 |
| namespace | 要查詢的集合 |
| indexFilterSet | 是否使用索引 |
| parsedQuery | 查詢條件,此處為x=1 |
| winningPlan | 最佳執行計劃 |
| stage | 查詢方式,常見的有COLLSCAN/全表掃描、IXSCAN/索引掃描、FETCH/根據索引去檢索檔案、SHARD_MERGE/合并分片結果、IDHACK/針對_id進行查詢 |
| filter | 過濾條件 |
| direction | 搜索方向 |
| rejectedPlans | 拒絕的執行計劃 |
| serverInfo | MongoDB服務器資訊 |
添加不同引數
explain()也接收不同的引數,通過設定不同引數我們可以查看更詳細的查詢計劃,
queryPlanner
是默認引數,添加queryPlanner引數的查詢結果就是我們勺ò復到的查詢結果,so,這里不再贅述,
executionStats
會回傳最佳執行計劃的一些統計資訊,如下:
COPYdb.zips.find({"pop":99999}).explain("executionStats")
我們發現增加了一個
executionStats的欄位列的資訊
COPY{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "zips-db.zips",
"indexFilterSet" : false,
"parsedQuery" : {
"pop" : {
"$eq" : 99999
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"pop" : 1
},
"indexName" : "pop_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"pop" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : true,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"pop" : [
"[99999.0, 99999.0]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 0,
"executionTimeMillis" : 1,
"totalKeysExamined" : 0,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 1,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"docsExamined" : 0,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 1,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"keyPattern" : {
"pop" : 1
},
"indexName" : "pop_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"pop" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : true,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"pop" : [
"[99999.0, 99999.0]"
]
},
"keysExamined" : 0,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
},
"serverInfo" : {
"host" : "localhost",
"port" : 27017,
"version" : "4.4.5",
"gitVersion" : "ff5cb77101b052fa02da43b8538093486cf9b3f7"
},
"ok" : 1
}
這里除了我們上文介紹到的一些引數之外,還多了executionStats引數,含義如下:
| 引數 | 含義 |
|---|---|
| executionSuccess | 是否執行成功 |
| nReturned | 回傳的結果數 |
| executionTimeMillis | 執行耗時 |
| totalKeysExamined | 索引掃描次數 |
| totalDocsExamined | 檔案掃描次數 |
| executionStages | 這個分類下描述執行的狀態 |
| stage | 掃描方式,具體可選值與上文的相同 |
| nReturned | 查詢結果數量 |
| executionTimeMillisEstimate | 預估耗時 |
| works | 作業單元數,一個查詢會分解成小的作業單元 |
| advanced | 優先回傳的結果數 |
| docsExamined | 檔案檢查數目,與totalDocsExamined一致 |
allPlansExecution:用來獲取所有執行計劃,結果引數基本與上文相同,這里就不再細說了,
慢查詢
在MySQL中,慢查詢日志是經常作為我們優化查詢的依據,那在MongoDB中是否有類似的功能呢?答案是肯定的,那就是開啟Profiling功能,該工具在運行的實體上收集有關MongoDB的寫操作,游標,資料庫命令等,可以在資料庫級別開啟該工具,也可以在實體級別開啟,該工具會把收集到的所有都寫入到system.profile集合中,該集合是一個capped collection,
慢查詢分析流程
慢查詢日志一般作為優化步驟里的第一步,通過慢查詢日志,定位每一條陳述句的查詢時間,比如超過了200ms,那么查詢超過200ms的陳述句需要優化,然后它通過 .explain() 決議影響行數是不是過大,所以導致查詢陳述句超過200ms,
所以優化步驟一般就是:
- 用慢查詢日志(system.profile)找到超過200ms的陳述句
- 然后再通過.explain()決議影響行數,分析為什么超過200ms
- 決定是不是需要添加索引
開啟慢查詢
Profiling級別說明
COPY0:關閉,不收集任何資料,
1:收集慢查詢資料,默認是100毫秒,
2:收集所有資料
針對資料庫設定
登錄需要開啟慢查詢的資料庫
COPYuse zips-db
查看慢查詢狀態
COPYdb.getProfilingStatus()
設定慢查詢級別
COPYdb.setProfilingLevel(2)

如果不需要收集所有慢日志,只需要收集小于100ms的慢日志可以使用如下命令
COPYdb.setProfilingLevel(1,200)
注意:
- 以上要操作要是在test集合下面的話,只對該集合里的操作有效,要是需要對整個實體有效,則需要在所有的集合下設定或則在開啟的時候開啟引數
- 每次設定之后回傳給你的結果是修改之前的狀態(包括級別、時間引數),
全域設定
在mongoDB啟動的時候加入如下引數
COPYmongod --profile=1 --slowms=200
或則在組態檔里添加2行:
COPYprofile = 1
slowms = 200
這樣就可以針對所有資料庫進行監控慢日志了
關閉Profiling
使用如下命令可以關閉慢日志
COPYdb.setProfilingLevel(0)

Profile 效率
Profiling功能肯定是會影響效率的,但是不太嚴重,原因是他使用的是system.profile 來記錄,而system.profile 是一個capped collection, 這種collection 在操作上有一些限制和特點,但是效率更高,
慢查詢分析
通過 db.system.profile.find() 查看當前所有的慢查詢日志
COPYdb.system.profile.find()

引數含義
COPY{
"op" : "query", #操作型別,有insert、query、update、remove、getmore、command
"ns" : "onroad.route_model", #操作的集合
"query" : {
"$query" : {
"user_id" : 314436841,
"data_time" : {
"$gte" : 1436198400
}
},
"$orderby" : {
"data_time" : 1
}
},
"ntoskip" : 0, #指定跳過skip()方法 的檔案的數量,
"nscanned" : 2, #為了執行該操作,MongoDB在 index 中瀏覽的檔案數, 一般來說,如果 nscanned 值高于 nreturned 的值,說明資料庫為了找到目標檔案掃描了很多檔案,這時可以考慮創建索引來提高效率,
"nscannedObjects" : 1, #為了執行該操作,MongoDB在 collection中瀏覽的檔案數,
"keyUpdates" : 0, #索引更新的數量,改變一個索引鍵帶有一個小的性能開銷,因為資料庫必須洗掉舊的key,并插入一個新的key到B-樹索引
"numYield" : 1, #該操作為了使其他操作完成而放棄的次數,通常來說,當他們需要訪問還沒有完全讀入記憶體中的資料時,操作將放棄,這使得在MongoDB為了放棄操作進行資料讀取的同時,還有資料在記憶體中的其他操作可以完成
"lockStats" : { #鎖資訊,R:全域讀鎖;W:全域寫鎖;r:特定資料庫的讀鎖;w:特定資料庫的寫鎖
"timeLockedMicros" : { #該操作獲取一個級鎖花費的時間,對于請求多個鎖的操作,比如對 local 資料庫鎖來更新 oplog ,該值比該操作的總長要長(即 millis )
"r" : NumberLong(1089485),
"w" : NumberLong(0)
},
"timeAcquiringMicros" : { #該操作等待獲取一個級鎖花費的時間,
"r" : NumberLong(102),
"w" : NumberLong(2)
}
},
"nreturned" : 1, // 回傳的檔案數量
"responseLength" : 1669, // 回傳位元組長度,如果這個數字很大,考慮值回傳所需欄位
"millis" : 544, #消耗的時間(毫秒)
"execStats" : { #一個檔案,其中包含執行 查詢 的操作,對于其他操作,這個值是一個空檔案, system.profile.execStats 顯示了就像樹一樣的統計結構,每個節點提供了在執行階段的查詢操作情況,
"type" : "LIMIT", ##使用limit限制回傳數
"works" : 2,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 1,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1, #是否為檔案結束符
"children" : [
{
"type" : "FETCH", #根據索引去檢索指定document
"works" : 1,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 1,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 0,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
{
"type" : "IXSCAN", #掃描索引鍵
"works" : 1,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 1,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 0,
"keyPattern" : "{ user_id: 1.0, data_time: -1.0 }",
"boundsVerbose" : "field #0['user_id']: [314436841, 314436841], field #1['data_time']: [1436198400, inf.0]",
"isMultiKey" : 0,
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 2,
"children" : [ ]
}
]
}
]
},
"ts" : ISODate("2015-10-15T07:41:03.061Z"), #該命令在何時執行
"client" : "10.10.86.171", #鏈接ip或則主機
"allUsers" : [
{
"user" : "martin_v8",
"db" : "onroad"
}
],
"user" : "martin_v8@onroad"
}
分析
如果發現 millis 值比較大,那么就需要作優化,
- 如果nscanned數很大,或者接近記錄總數(檔案數),那么可能沒有用到索引查詢,而是全表掃描,
- 如果 nscanned 值高于 nreturned 的值,說明資料庫為了找到目標檔案掃描了很多檔案,這時可以考慮創建索引來提高效率,
system.profile補充
‘type’的回傳引數說明
COPYCOLLSCAN #全表掃描
IXSCAN #索引掃描
FETCH #根據索引去檢索指定document
SHARD_MERGE #將各個分片回傳資料進行merge
SORT #表明在記憶體中進行了排序(與老版本的scanAndOrder:true一致)
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的回傳
對于普通查詢,我們最希望看到的組合有這些
COPYFetch+IDHACK
Fetch+ixscan
Limit+(Fetch+ixscan)
PROJECTION+ixscan
SHARDING_FILTER+ixscan
等
不希望看到包含如下的type
COPYCOLLSCAN(全表掃),SORT(使用sort但是無index),不合理的SKIP,SUBPLA(未用到index的$or)
最后說一句(求關注,別白嫖我)
如果這篇文章對您有所幫助,或者有所啟發的話,幫忙掃描下發二維碼關注一下,您的支持是我堅持寫作最大的動力,
求一鍵三連:點贊、轉發、在看,
本文由
傳智教育博學谷狂野架構師教研團隊發布,如果本文對您有幫助,歡迎
關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力,轉載請注明出處!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/549734.html
標籤:其他
上一篇:Python property、setter、deleter
下一篇:Go語言入門5(map哈希表)
