MongoDB慢查詢與索引實(shí)例詳解
MongoDB慢查詢
慢查詢分析
- 開(kāi)啟內(nèi)置的慢查詢分析器
db.setProfilingLevel(n,m),n的取值可選0,1,2
- 0:表示不記錄
- 1:表示記錄慢速操作,如果值為1,m需要傳慢查詢的閾值,單位為ms
- 2:表示記錄所有的讀寫(xiě)操作
示例:
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-05Centos 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-03MongoDB開(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-02MongoDB 數(shù)據(jù)庫(kù)的命名、設(shè)計(jì)規(guī)范詳解
隨著MongoDB的普及和使用量的快速增長(zhǎng),為了規(guī)范使用,便于管理和獲取更高的性能,整理此文檔2020-02-02MongoDB模糊查詢操作案例詳解(類關(guān)系型數(shù)據(jù)庫(kù)的 like 和 not like)
這篇文章主要介紹了MongoDB的模糊查詢操作(類關(guān)系型數(shù)據(jù)庫(kù)的 like 和 not like) ,本文通過(guò)代碼案例分析給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,,需要的朋友可以參考下2019-07-07