MongoDB 索引創(chuàng)建和查詢優(yōu)化的方法
2.1 批量插入數(shù)據(jù)
單條數(shù)據(jù)插入
db.collection.insertOne()
多條數(shù)據(jù)插入
db.collection.insertMany()
db.inventory.insertMany( [
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);插入數(shù)據(jù)
use testdb
for(var i =1; i<10; i++) db.users.insert({id:i,name: "zhangsan"+i,age: 100+i})2.2 查詢選擇器
2.2.1 常規(guī)查詢方式
最簡(jiǎn)單的查詢語(yǔ)句為:db.customers.find(),按照插入的順序返回前20個(gè)文檔,如果 記錄總數(shù)比20大,則我們可以通過(guò)命令“it”獲取更多文檔。
> db.users.find({id:9})
精確匹配選擇器,返回包含鍵值對(duì)id:9的文檔。
> db.users.find({name:"xiaoming",age:101})
精確匹配選擇器,但查詢條件是要返回同時(shí)匹配鍵值對(duì){name:"xiaoming",age:101}的文檔。
> db.users.find({age:{$lt:102}})
$lt表示的是小于
> db.users.find({age:{$lte:102}})
$lte表示的小于或等于
> db.users.find({age:{$gt:105}})
$gt表示的是大于
> db.users.find({age:{$gte:105}})
$gte表示的是大于或等于
> db.users.find({age:{$lt:120,$gte:105}})
范圍選擇器,age:{$lt:120,$gte:119}表示的是小于120,大于或等于119
> db.users.find({id:{$in:[1,2]}})
表示返回key的值在某些value范圍內(nèi)
> db.users.find({id:{$nin:[1,2]}})
$nin表示返回key的值不在某些value范圍內(nèi),$nin是一種比較低效的査詢選擇器,它會(huì)進(jìn)行全表掃描,因此最好不要單獨(dú)使用$nin
> db.users.find({id:{$ne:1}})
$ne表示不等于。單獨(dú)使用$ne,它也不會(huì)利用索引的優(yōu)勢(shì),反而會(huì)進(jìn)行全表掃描,我們最好與其他查詢選擇器配合使用。
> db.users.find({$or:[{id:11},{age:109}] })
$or表示或運(yùn)算的選擇器,主要用于對(duì)兩個(gè)不同key對(duì)應(yīng)的文檔進(jìn)行連接。
> db.users.find({$and:[{id:1},{age:109}]})
$and表示與運(yùn)算的選擇器,對(duì)于兩個(gè)不同的key,要同時(shí)滿足條件。
> db.users.find({id:{$exists:ture}})
$exists與關(guān)系數(shù)據(jù)庫(kù)中的exists不一樣,因?yàn)镸ongoDB的表結(jié)構(gòu)不是固定的,有的時(shí)候需要返回包含有某個(gè)字段的所有記錄或者不包含某個(gè)字段的所有記錄。2.2.2 索引和查詢優(yōu)化
索引是個(gè)與數(shù)據(jù)存儲(chǔ)和査詢相關(guān)的古老話題,目的只有一個(gè):“提高數(shù)據(jù)獲取的性能”。我們知道一本書的前面幾頁(yè)肯定會(huì)有一個(gè)目錄,這個(gè)目錄式的索引能使我們快速査詢想看的內(nèi)容。索引保存在哪里,是個(gè)什么樣的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)領(lǐng)域的索引無(wú)外乎也是這兩個(gè)主題。

數(shù)據(jù)庫(kù)保存記錄的機(jī)制是建立在文件系統(tǒng)上的,索引也是以文件的形式存儲(chǔ)在磁盤上,在數(shù)據(jù)庫(kù)中用到的最多的索引結(jié)構(gòu)就是B樹(shù)。盡管索引在數(shù)據(jù)庫(kù)領(lǐng)域是不可缺少的,但是對(duì)一個(gè)表建立過(guò)多的索引也會(huì)帶來(lái)一些問(wèn)題,索引的建立要花費(fèi)系統(tǒng)時(shí)間,同時(shí)索引文件也會(huì)占用磁盤空間。
**索引通常能夠極大的提高查詢的效率,如果沒(méi)有索引,MongoDB在讀取數(shù)據(jù)時(shí)必須掃描集合中的每個(gè)文件并選取那些符合查詢條件的記錄。**掃描全集合的查詢效率是非常低的,特別在處理大量的數(shù)據(jù)時(shí),查詢可以要花費(fèi)幾十秒甚至幾分鐘,這對(duì)網(wǎng)站的性能是非常致命的。
索引是特殊的數(shù)據(jù)結(jié)構(gòu),索引存儲(chǔ)在一個(gè)易于遍歷讀取的數(shù)據(jù)集合中,索引是對(duì)數(shù)據(jù)庫(kù)表中一列或多列的值進(jìn)行排序的一種結(jié)構(gòu)。
MongoDB索引的數(shù)據(jù)結(jié)構(gòu)也是B+樹(shù),它能存儲(chǔ)一小部分集合的數(shù)據(jù),具體來(lái)說(shuō)就是存儲(chǔ)集合中建有索引的一個(gè)或多個(gè)字段的值,而且按照值的升序或降序排列。對(duì)于一個(gè)查詢來(lái)說(shuō),如果存在合適的索引,MongoDB能夠利用這個(gè)索引減少文檔的掃描數(shù)量。
如圖所示查詢低于30歲的用戶,不用去掃描全部文檔,通過(guò)索引快速返回結(jié)果,這樣查詢的效率是很高的。

單字段索引
MongoDB默認(rèn)為所有集合都創(chuàng)建了一個(gè)_id字段的單字段索引,而且這個(gè)索引是唯一的,不能被刪除,_id字段作為一個(gè)集合的主鍵,值是唯一的,對(duì)于一個(gè)集合來(lái)說(shuō),也可以在其他字段上創(chuàng)建單字段的唯一索引。
創(chuàng)建單一鍵索引:db.collection.createIndex( { : } ),其中 是你要?jiǎng)?chuàng)建索引的字段名, 是索引類型,例如:1(升序)或 -1(降序)。
插入數(shù)據(jù)
for(var i = 1;i < 10;i++) db.custoners.insert({name:"zhangsan"+i,province:"liaoning"})
for(var i = 1;i < 10;i++) db.customers.insert({name:"lisi"+i,province:"fujian"})
for(var i = 1;i < 10;i++) db.customers.insert({name:"niuer"+i,province:"guangdong"})
for(var i = 1;i < 10;i++) db.customers.insert({name:"wangwu"+i,province:"Hunan"})
for(var i = 1;i < 10;i++) db.customers.insert({name:"liyi"+i,province:"Sichuan"})
創(chuàng)建索引
建立單字段唯一索引或者去掉{unique:true}選項(xiàng)就是一個(gè)普通的單字段索引
db.customers.createIndex({name:1},{unique:true})
1表示升序創(chuàng)建索引,-1表示降序創(chuàng)建索引。

通過(guò)explain查看執(zhí)行計(jì)劃
MongoDB中的explain()方法用于顯示查詢執(zhí)行計(jì)劃,它可以幫助我們了解MongoDB如何執(zhí)行一個(gè)查詢。
執(zhí)行explain()方法后,MongoDB將返回一個(gè)對(duì)象,該對(duì)象描述了查詢執(zhí)行的過(guò)程,包括查詢的階段、輸入輸出、使用的索引等信息。
這個(gè)對(duì)象通常包含以下字段:
- stages:查詢執(zhí)行的階段列表,每個(gè)階段描述了查詢的一部分執(zhí)行過(guò)程。
- input:查詢輸入的文檔數(shù)量。
- output:查詢輸出的文檔數(shù)量。
- millis:查詢執(zhí)行的時(shí)間(毫秒)。
- executionStats:更詳細(xì)的執(zhí)行統(tǒng)計(jì)信息。
其中,stages字段是最重要的,它描述了查詢從開(kāi)始到結(jié)束的所有階段。每個(gè)階段都有一個(gè)type字段,描述了這個(gè)階段的類型,比如:
- COLLSCAN:掃描整個(gè)集合。
- IXSCAN:掃描索引。
- SHARD_MERGE:合并從多個(gè)分片返回的結(jié)果。
通過(guò)查看stages字段,我們可以了解查詢使用了哪些索引,是否有更好的優(yōu)化方案等。
需要注意的是,explain()方法返回的結(jié)果包含了大量的詳細(xì)信息,對(duì)于普通用戶來(lái)說(shuō)可能比較難以理解。通常我們只需要關(guān)注stages字段,以及其中的type值為IXSCAN的階段,因?yàn)檫@是查詢執(zhí)行的關(guān)鍵階段。
沒(méi)走索引查詢,查詢使用了COLLSCAN階段掃描了整個(gè)集合,但是并沒(méi)有使用到索引。

命中索引進(jìn)行查詢

復(fù)合索引
創(chuàng)建復(fù)合索引:db.collection.createIndex( { : , : } ),其中 和 是你要?jiǎng)?chuàng)建索引的字段名, 和 是索引類型,
例如:1(升序)或 -1(降序)。
請(qǐng)注意,創(chuàng)建索引可能需要一些時(shí)間,具體取決于你的數(shù)據(jù)量及系統(tǒng)性能。同時(shí),創(chuàng)建過(guò)多的索引可能會(huì)對(duì)寫入性能產(chǎn)生負(fù)面影響,因此需要謹(jǐn)慎考慮。

> db.customers.find({"name" : "liyi6", "province" : "Sichuan"}).explain()
{
"explainVersion" : "1",
"queryPlanner" : {
"namespace" : "sample_mflix.customers",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"name" : {
"$eq" : "liyi6"
}
},
{
"province" : {
"$eq" : "Sichuan"
}
}
]
},
"queryHash" : "AE1EB7A5",
"planCacheKey" : "C0AA3338",
"maxIndexedOrSolutionsReached" : false,
"maxIndexedAndSolutionsReached" : false,
"maxScansToExplodeReached" : false,
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1,
"province" : 1
},
"indexName" : "name_1_province_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"name" : [ ],
"province" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"name" : [
"[\"liyi6\", \"liyi6\"]"
],
"province" : [
"[\"Sichuan\", \"Sichuan\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"command" : {
"find" : "customers",
"filter" : {
"name" : "liyi6",
"province" : "Sichuan"
},
"$db" : "sample_mflix"
},
"serverInfo" : {
"host" : "13727b89dec5",
"port" : 27017,
"version" : "5.0.5",
"gitVersion" : "d65fd89df3fc039b5c55933c0f71d647a54510ae"
},
"serverParameters" : {
"internalQueryFacetBufferSizeBytes" : 104857600,
"internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
"internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
"internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
"internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
"internalQueryProhibitBlockingMergeOnMongoS" : 0,
"internalQueryMaxAddToSetBytes" : 104857600,
"internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
},
"ok" : 1
}數(shù)組的多鍵索引
注意,創(chuàng)建索引可能需要一些時(shí)間,具體取決于你的數(shù)據(jù)量和系統(tǒng)性能。同時(shí),創(chuàng)建過(guò)多的索引可能會(huì)對(duì)寫入性能產(chǎn)生負(fù)面影響,因此需要謹(jǐn)慎考慮。
tags 的數(shù)組字段為例,展示多鍵索
> for(var i = 1;i < 10;i++) db.ccustomers.insert({name:"liyi"+i,"tags": ["sports", "music", "movies"]})
WriteResult({ "nInserted" : 1 })
> for(var i = 1;i < 10;i++) db.ccustomers.insert({name:"liyao"+i,"tags": ["sports", "movies","games","read"]})
WriteResult({ "nInserted" : 1 })
> for(var i = 1;i < 10;i++) db.ccustomers.insert({name:"lisi"+i,"tags": ["sports", "music", "movies"]})
WriteResult({ "nInserted" : 1 })
> for(var i = 1;i < 10;i++) db.ccustomers.insert({name:"liyiyi"+i,"tags": ["sports", "movies","write"]})
WriteResult({ "nInserted" : 1 })
> db.ccustomers.createIndex({"tags":1})
{
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"createdCollectionAutomatically" : false,
"ok" : 1
}explain

查詢結(jié)果

查詢優(yōu)化
MongoDB查詢優(yōu)化的方法主要有以下幾點(diǎn):
- 用合適的索引:索引是提高查詢性能的關(guān)鍵。MongoDB支持多種索引類型,如單一鍵索引、復(fù)合索引、文本索引、地理空間索引等。在設(shè)計(jì)數(shù)據(jù)庫(kù)時(shí),應(yīng)根據(jù)數(shù)據(jù)的特性和查詢需求選擇合適的索引類型。同時(shí),也要注意索引的使用,盡量使用已經(jīng)創(chuàng)建的索引,避免全集合掃描。
- 優(yōu)化查詢語(yǔ)句:查詢語(yǔ)句的設(shè)計(jì)也會(huì)影響查詢性能。應(yīng)盡量避免使用不等于操作符、模運(yùn)算符等導(dǎo)致全集合掃描的操作符。同時(shí),應(yīng)使用投影查詢,減少返回的字段,減少數(shù)據(jù)傳輸和處理的開(kāi)銷。
- 批量操作:批量操作可以減少IO操作次數(shù),提高性能。例如批量插入、批量更新、批量刪除等。
- 使用緩存:使用緩存可以避免重復(fù)查詢,提高查詢性能。
- 優(yōu)化數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)也會(huì)影響查詢性能。應(yīng)盡量選擇合適的數(shù)據(jù)類型,避免使用嵌套文檔和數(shù)組,提高查詢效率。
- 調(diào)整系統(tǒng)參數(shù):根據(jù)系統(tǒng)性能和硬件配置調(diào)整MongoDB的配置參數(shù),例如內(nèi)存、磁盤、網(wǎng)絡(luò)等參數(shù)。
- 使用分析工具:使用MongoDB提供的分析工具,如explain()、profile()等,可以了解查詢性能,找出優(yōu)化點(diǎn)。
位慢查詢的方法是打開(kāi)數(shù)據(jù)庫(kù)的監(jiān)視功能,它默認(rèn)是關(guān)閉的,我們可以通過(guò)下面的命令打開(kāi)。
db.setProfilingLevel(level,[ slowms])
參數(shù):
level是監(jiān)視級(jí)別。
值為0表示關(guān)閉數(shù)據(jù)庫(kù)的監(jiān)視功能
值為1表示只記錄慢查詢
值為2表示記錄所有的操作
slowms為可選參數(shù),設(shè)定慢查詢的閾值。
所有監(jiān)視的結(jié)果都將保存到一個(gè)特殊的集合system.profile中。
> db.setProfilingLevel(2)
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
> db.system.profile.find()實(shí)操記錄:
一個(gè)走索引和沒(méi)有走索引的查詢

打開(kāi)查看查詢記錄:


2.3 總結(jié)
MongoDB可以在一個(gè)集合上建立一個(gè)或多個(gè)索引,而且必須為在字段_id建立一個(gè)索引,建索引的目的與關(guān)系數(shù)據(jù)庫(kù)一樣,就是為了提高對(duì)數(shù)據(jù)庫(kù)的查詢效率;
一旦索引創(chuàng)建好,MongoDB會(huì)自動(dòng)地根據(jù)數(shù)據(jù)的變化維護(hù)索引,如果索引太大而不能全部保存在內(nèi)存中,將被移到磁盤文件上,這樣會(huì)影響查詢性能,因此要時(shí)刻監(jiān)控索引的大小,保證合適的索引在內(nèi)存中;
監(jiān)控一個(gè)查詢是否用到索引,可以在查詢語(yǔ)句后用explain命令或profile()方式進(jìn)行監(jiān)控。并不是所有的字段都要建立索引,我們應(yīng)該根據(jù)自己業(yè)務(wù)所涉及的查詢,建立合適的索引;
如果系統(tǒng)有大量的寫操作,由于需要維護(hù)索引的變化,會(huì)導(dǎo)致系統(tǒng)性能降低。我們?cè)趯?duì)大數(shù)據(jù)建立索引時(shí)最好在后臺(tái)進(jìn)行,否則會(huì)導(dǎo)致數(shù)據(jù)庫(kù)停止響應(yīng)。要注意雖然我們?cè)谀承┳侄紊辖怂饕?,但是查詢時(shí)可能用不上索引,如使用 n e 和 ne和 ne和nin表達(dá)式等。
到此這篇關(guān)于MongoDB 索引創(chuàng)建和查詢優(yōu)化的文章就介紹到這了,更多相關(guān)MongoDB 索引和查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MongoDB數(shù)據(jù)庫(kù)查詢性能提高40倍的經(jīng)歷分享
大家在使用 MongoDB 的時(shí)候有沒(méi)有碰到過(guò)性能問(wèn)題呢?下面這篇文章主要給大家分享了MongoDB數(shù)據(jù)庫(kù)查詢性能提高40倍的經(jīng)歷,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-02-02
Mongodb 3.2.9開(kāi)啟用戶權(quán)限認(rèn)證問(wèn)題的步驟詳解
這篇文章主要給大家介紹了關(guān)于Mongodb 3.2.9開(kāi)啟用戶權(quán)限認(rèn)證問(wèn)題的詳細(xì)步驟,通過(guò)開(kāi)啟權(quán)限認(rèn)證,會(huì)對(duì)大家的Mongodb更加保護(hù)的安全些,文中將步驟介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-08-08
教大家8天學(xué)通MongoDB——第一天 基礎(chǔ)入門篇
MongoDB是目前非常流行的一種非關(guān)系型數(shù)據(jù)庫(kù)(NoSQL),因其操作簡(jiǎn)單、完全免費(fèi)、源碼公開(kāi)等特點(diǎn),受到了IT從業(yè)人員的青睞,并被廣泛部署于實(shí)際的生產(chǎn)環(huán)境中。本文教大家8天學(xué)通MongoDB——第一天 基礎(chǔ)入門篇,感興趣的朋友一起來(lái)了解了解吧2015-09-09
window平臺(tái)安裝MongoDB數(shù)據(jù)庫(kù)圖文詳解
本篇文章主要介紹了window平臺(tái)安裝MongoDB數(shù)據(jù)庫(kù)圖文詳解,主要介紹window下面安裝mogod的步驟和使用細(xì)節(jié)。感興趣的小伙伴們可以參考一下。2016-11-11
MongoDB在系統(tǒng)數(shù)據(jù)庫(kù)local中無(wú)法創(chuàng)建用戶的解決辦法
這篇文章主要給大家介紹了關(guān)于MongoDB在系統(tǒng)數(shù)據(jù)庫(kù)local中無(wú)法創(chuàng)建用戶的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11
mongodb中隨機(jī)獲取1條記錄的實(shí)現(xiàn)方法
這篇文章運(yùn)用實(shí)例給大家演示了如何在mongodb中隨機(jī)獲取1條記錄,文中介紹的很詳細(xì),有需要的朋友們可以參考借鑒。下面來(lái)一起看看吧。2016-09-09

