MongoDB慢查詢與索引實例詳解
MongoDB慢查詢
慢查詢分析
- 開啟內(nèi)置的慢查詢分析器
db.setProfilingLevel(n,m),n的取值可選0,1,2
- 0:表示不記錄
- 1:表示記錄慢速操作,如果值為1,m需要傳慢查詢的閾值,單位為ms
- 2:表示記錄所有的讀寫操作
示例:
db.setProfilingLevel(1,3)
- 查詢監(jiān)控結(jié)果
db.system.profile.find().sort({millis:-1}).limit(3)
MongoDB索引
什么是索引?
索引是一種單獨的、物理的對數(shù)據(jù)庫表中一列或多列的值進行排序的一種存儲結(jié)構(gòu),它是某個表中一列或若干列值的集合和相應(yīng)的指向表中物理標(biāo)識這些值的數(shù)據(jù)頁的邏輯指針清單。索引的作用相當(dāng)于圖書的目錄,可以根據(jù)目錄中的頁碼快速找到所需的內(nèi)容。索引目標(biāo)是提高數(shù)據(jù)庫的查詢效率,沒有索引的話,查詢會進行全表掃描(scaneverydocumentinacollection),數(shù)據(jù)量大時嚴(yán)重降低了查詢效率。默認情況下Mongo在一個集合(collection)創(chuàng)建時,自動地對集合的_id創(chuàng)建了唯一索引。
索引結(jié)構(gòu)
MongoDB的索引結(jié)構(gòu)為B樹
B樹非葉子節(jié)點也存了數(shù)據(jù),查詢效率不固定,最好的情況是O(1),在單次查詢的情況下平均性能是優(yōu)于B+樹的。而MongoDB是被作為一個單一查詢比較多,遍歷數(shù)據(jù)比較少的一個定位。所以采用了B樹。
那為什么不用單次性能更好的Hash結(jié)構(gòu)呢?
因為雖然遍歷數(shù)據(jù)的情況較少,但是對于遍歷數(shù)據(jù)也需要有相對較好的性能支持。Hash這種性能表現(xiàn)較為極端的數(shù)據(jù)結(jié)構(gòu)往往只能在簡單、極端的場景下使用。
索引分類
- 單鍵索引
MongoDB支持所有數(shù)據(jù)類型中的單個字段索引,并且可以在文檔的任何字段定義。對于單個字段索引,索引鍵的排序順序無關(guān)緊要,因為MongoDB可以在任一方向讀取索引。
db.集合名.createIndex({"字段名":排序方式})
示例:
db.user.createIndex({"name":1})
創(chuàng)建后可以通過查詢索引命令查看是否創(chuàng)建成功。
db.user.getIndexes()
- 唯一索引
設(shè)置unique為true。示例:
db.book.createIndex({title:1},{unique:true})
- 過期索引TTL
TTL索引是MongoDB中一種特殊的索引,可以支持文檔在一定時間之后自動過期刪除,目前TTL索引只能在單字段上建立,并且字段類型必須是日期類型。
db.集合名.createIndex({"日期字段":排序方式}, {expireAfterSeconds: 秒數(shù)})
示例:
db.user.createIndex({"bithday":1}, {expireAfterSeconds: 10})
創(chuàng)建過期索引后,有bithday字段的文檔會在約10秒后自動刪除。
- 復(fù)合索引
通常我們需要在多個字段上進行搜索,如果是這種情況,可以考慮使用復(fù)合索引。復(fù)合索引支持基于多個字段的索引,這擴展了索引的概念并將它們擴展到索引中的更大域。
建立復(fù)合索引需要注意:字段順序和索引方向。它也是遵循最左前綴原則。
db.集合名.createIndex( { "字段名1" : 排序方式, "字段名2" : 排序方式 } )
- 多鍵索引
針對屬性包含數(shù)組數(shù)據(jù)的情況,MongoDB支持針對數(shù)組中每一個element創(chuàng)建索引,支持Strings、numbers、nested documents。
示例:
//type是集合類型的數(shù)據(jù),創(chuàng)建的就是多鍵索引
db.book.insert({title:"java",type:["技術(shù)","IT"]})
db.book.createIndex({type:1})
- 哈希索引
針對屬性的哈希值進行索引查詢,當(dāng)要使用Hashed Index時,MongoDB能夠自動計算hash值來進行查詢。
db.集合.createIndex({"字段": "hashed"})
- 地理空間索引
針對地理空間坐標(biāo)數(shù)據(jù)創(chuàng)建索引。2dsphere索引:用于存儲和查找球面上的點。
2d索引:用于存儲和查找平面上的點。
db.集合名.ensureIndex({字段名:"2dsphere"})
示例:
//插入數(shù)據(jù)
db.company.insert({
loc:{type:"Point",coordinates:[116.482451,39.914176]},
name:"大望路",
category:"Parks"
})
//創(chuàng)建索引
db.company.ensureIndex({loc:"2dsphere"})
//查詢范圍內(nèi)的數(shù)據(jù)
db.company.find({
"loc":{
"$geoWithin":{
"$center":[[116.482450,39.914176],0.05]
}
}
})
//距離指定位置最近的2個點
db.company.aggregate([
{
$geoNear: {
near: {
type: "Point",
coordinates: [ 116.472451,39.814176]
},
key:"loc",
distanceField: "dist.calculated",
spherical: true
}
},
{
$limit: 2
}
])
索引管理
- 創(chuàng)建索引并在后臺運行
有時數(shù)據(jù)量大的時候,創(chuàng)建索引的動作是比較耗費時間的,這時后臺運行就比較有用了。
db.COLLECTION_NAME.createIndex({"字段":排序方式}, {background: true});
- 查詢某個集合的索引
db.COLLECTION_NAME.getIndexes()
- 查看索引大小
db.COLLECTION_NAME.totalIndexSize()
- 索引重建
db.COLLECTION_NAME.reIndex()
- 索引刪除
db.COLLECTION_NAME.dropIndex("INDEX-NAME")
db.COLLECTION_NAME.dropIndexes()
注意: _id 對應(yīng)的索引是刪除不了的
Explain分析
explain()是一個查詢分析的方法,它還可以接收不同的參數(shù)來查看更詳細的查詢計劃。
簡單示例:
db.user.find().explain()
db.user.find({name:"test1"}).explain("executionStats")參數(shù)介紹:
- queryPlanner:queryPlanner是默認參數(shù),具體執(zhí)行計劃信息參考下面的表格
- executionStats:executionStats會返回執(zhí)行計劃的一些統(tǒng)計信息(有些版本中和allPlansExecution等同)。
- allPlansExecution:allPlansExecution用來獲取所有執(zhí)行計劃,結(jié)果參數(shù)基本與上文相同
- queryPlanner參數(shù)查詢返回值含義
| 參數(shù) | 含義 |
|---|---|
| plannerVersion | 查詢計劃版本 |
| namespace | 要查詢的集合(該值返回的是該query所查詢的表)數(shù)據(jù)庫.集合 |
| indexFilterSet | 針對該query是否有indexFilter |
| parsedQuery | 查詢條件 |
| winningPlan | 被選中的執(zhí)行計劃 |
| winningPlan.stage | 被選中執(zhí)行計劃的stage(查詢方式),常見的有:COLLSCAN/全表掃描:(應(yīng)該知道就是CollectionScan,就是所謂的“集合掃描”,和mysql中tablescan/heapscan類似,這個就是所謂的性能最爛最無奈的由來)、IXSCAN/索引掃描:(是IndexScan,這就說明我們已經(jīng)命中索引了)、FETCH/根據(jù)索引去檢索文檔、SHARD_MERGE/合并分片結(jié)果、IDHACK/針對_id進行查詢等 |
| winningPlan.inputStage | 用來描述子stage,并且為其父stage提供文檔和索引關(guān)鍵字。 |
| winningPlan.stage的child stage | 如果此處是IXSCAN,表示進行的是index scanning。 |
| winningPlan.keyPattern | 所掃描的index內(nèi)容 |
| winningPlan.indexName | winning plan所選用的index。 |
| winningPlan.isMultiKey | 是否是Multikey,此處返回是false,如果索引建立在array上,此處將是true。 |
| winningPlan.direction | 此query的查詢順序,此處是forward,如果用了.sort({字段:-1})將顯示backward。 |
| filter | 過濾條件 |
| winningPlan.indexBounds | winningplan所掃描的索引范圍,如果沒有制定范圍就是[MaxKey,MinKey],這主要是直接定位到mongodb的chunck中去查找數(shù)據(jù),加快數(shù)據(jù)讀取。 |
| rejectedPlans | 被拒絕的執(zhí)行計劃的詳細返回,其中具體信息與winningPlan的返回中意義相同,故不在此贅述 |
| serverInfo | MongoDB服務(wù)器信息 |
- executionStats參數(shù)查詢返回值含義
| 參數(shù) | 含義 |
|---|---|
| executionSuccess | 是否執(zhí)行成功 |
| nReturned | 返回的文檔數(shù) |
| executionTimeMillis | 執(zhí)行耗時 |
| totalKeysExamined | 索引掃描次數(shù) |
| totalDocsExamined | 文檔掃描次數(shù) |
| executionStages | 這個分類下描述執(zhí)行的狀態(tài) |
| stage | 掃描方式,具體可選值與上文的相同 |
| nReturned | 查詢結(jié)果數(shù)量 |
| executionTimeMillisEstimate | 檢索document獲得數(shù)據(jù)的時間 |
| inputStage.executionTimeMillisEstimate | 該查詢掃描文檔 index所用時間 |
| works | 工作單元數(shù),一個查詢會分解成小的工作單元 |
| advanced | 優(yōu)先返回的結(jié)果數(shù) |
| docsExamined | 文檔檢查數(shù)目,與totalDocsExamined一致。檢查了總共的document個數(shù),而從返回上面的nReturned數(shù)量 |
這么多返回值我們怎么分析呢?
首先我們先造點數(shù)據(jù):
for(var i=0;i<100000;i++){
db.user.insert({
name:"test"+i,
explectSalary:10+i
})
}
查詢耗時115
db.user.find({name:'test1'}).explain("allPlansExecution")
然后創(chuàng)建索引
db.user.createIndex({name:1})再次查詢,查看耗時變?yōu)榱?。速度直線飆升。我們再對返回結(jié)果做一個分析:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.user",
"indexFilterSet" : false,
"parsedQuery" : {
"name" : {
"$eq" : "test1"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1
},
"indexName" : "name_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"test1\", \"test1\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 2,
"executionTimeMillis" : 2,
"totalKeysExamined" : 2,
"totalDocsExamined" : 2,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 3,
"advanced" : 2,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"docsExamined" : 2,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 3,
"advanced" : 2,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"keyPattern" : {
"name" : 1
},
"indexName" : "name_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"test1\", \"test1\"]"
]
},
"keysExamined" : 2,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
},
"allPlansExecution" : [ ]
},
"serverInfo" : {
"host" : "10.0.3.15",
"port" : 27017,
"version" : "4.2.21",
"gitVersion" : "b0aeed9445ff41af07449fa757e1f231bce990b3"
},
"ok" : 1
}折疊
重要參數(shù)介紹:
- executionStats.executionTimeMillis 整體查詢時間
- executionStats.executionStages.executionTimeMillisEstimate 該查詢檢索document獲得數(shù)據(jù)的時間
- executionStats.inputStage.executionTimeMillisEstimate 該查詢掃描文檔index所用的時間
- executionStats.nReturned 查詢返回的條數(shù)
- executionStats.totalKeysExamined:索引掃描條數(shù)
- executionStats.totalDocsExamined:文檔掃描條數(shù)
對于一個查詢,我們最理想的狀態(tài)是:nReturned=totalKeysExamined=totalDocsExamined
- stage狀態(tài):它的值有很多,如下所示:
類型列舉如下:
- COLLSCAN:全表掃描
- IXSCAN:索引掃描
- FETCH:根據(jù)索引去檢索指定document
- SHARD_MERGE:將各個分片返回數(shù)據(jù)進行merge
- SORT:表明在內(nèi)存中進行了排序
- LIMIT:使用limit限制返回數(shù)
- SKIP:使用skip進行跳過
- IDHACK:針對_id進行查詢
- SHARDING_FILTER:通過mongos對分片數(shù)據(jù)進行查詢
- COUNT:利用db.coll.explain().count()之類進行count運算
- TEXT:使用全文索引進行查詢時候的stage返回
- PROJECTION:限定返回字段時候stage的返回
還有的是上面的組合
- Fetch+IDHACK
- Fetch+IXSCAN
- Limit+(Fetch+IXSCAN)
- PROJECTION+IXSCAN
- SHARDING_FITER+IXSCAN
總結(jié)
到此這篇關(guān)于MongoDB慢查詢與索引的文章就介紹到這了,更多相關(guān)MongoDB慢查詢與索引內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Centos 7.2中MongoDB數(shù)據(jù)庫的安裝與卸載教程
這篇文章主要給大家介紹了關(guān)于在Centos 7.2中MongoDB數(shù)據(jù)庫的安裝與卸載的相關(guān)資料,文中還給大家總結(jié)了在過程中可能會遇到的一些問題的解決方法,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03
MongoDB 數(shù)據(jù)庫的命名、設(shè)計規(guī)范詳解
隨著MongoDB的普及和使用量的快速增長,為了規(guī)范使用,便于管理和獲取更高的性能,整理此文檔2020-02-02
MongoDB模糊查詢操作案例詳解(類關(guān)系型數(shù)據(jù)庫的 like 和 not like)
這篇文章主要介紹了MongoDB的模糊查詢操作(類關(guān)系型數(shù)據(jù)庫的 like 和 not like) ,本文通過代碼案例分析給大家介紹的非常詳細,具有一定的參考借鑒價值,,需要的朋友可以參考下2019-07-07

