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

