索引是特殊的數據結構,索引存儲在一個易於遍歷讀取的數據集合中( 索引存儲在特定字段或字段集的值),並且是使用了B-tree結構。索引能夠極大程度提高MongoDB查詢效率。
若是沒有索引,MongoDB必須執行全集合collections掃描,即掃描集合中的每一個文檔,選取符合查詢條件的文檔document。 若是查詢時存在適當的索引,MongoDB可使用索引來限制它必須查詢的文檔document的數量,特別是在處理大量數據時,因此選擇正確的索引是很關鍵的、重要的。 mongodb
建立索引,須要考慮的問題:數據庫
索引限制:數組
索引建立使用createIndex()方法,格式以下:服務器
db.collection.createIndex(<key and index type specification>,<options>)
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. |
查看Collection中全部索引,格式以下:ide
db.collection.getIndexes()
刪除Collection中的索引:格式以下:函數
db.collection.dropIndexes() //刪除全部索引 db.collection.dropIndex() //刪除指定的索引
索引的默認名稱是索引鍵和索引中每一個鍵的value1或-1,形式index_name+1/-1,好比:性能
db.products.createIndex( { item: 1, quantity: -1 } )----索引名稱爲item_1_quantity_-1
也能夠指定索引名稱:學習
db.products.createIndex( { item: 1, quantity: -1 } , { name: "inventory" } ) ----索引名稱爲inventory
方法 | 解析 |
---|---|
db.currentOp() | 查看索引建立過程 |
db.killOp(opid) | 終止索引建立,其中-opid爲操做id |
形式 | 解析 |
---|---|
$indexStats | 獲取索引訪問信息 |
explain() | 返回查詢狀況:在executionStats模式下使用db.collection.explain()或cursor.explain()方法返回有關查詢過程的統計信息,包括使用的索引,掃描的文檔數以及查詢處理的時間(以毫秒爲單位)。 |
Hint() | 控制索引,例如要強制MongoDB使用特定索引進行db.collection.find()操做,請使用hint()方法指定索引 |
MongoDB提供了許多索引使用和操做的度量標準,在分析數據庫的索引使用時可能須要考慮這些度量標準,以下所示:優化
形式 | 解析 |
---|---|
metrics.queryExecutor.scanned | 在查詢和查詢計劃評估期間掃描的索引項的總數 |
metrics.operation.scanAndOrder | 返回沒法使用索引執行排序操做的已排序數字的查詢總數 |
collStats.totalIndexSize | 全部索引的總大小。 scale參數會影響此值。若是索引使用前綴壓縮(這是WiredTiger的默認值),則返回的大小將反映計算總計時任何此類索引的壓縮大小。 |
collStats.indexSizes | 指定集合collection上每一個現有索引的鍵和大小。 scale參數會影響此值 |
dbStats.indexes | 包含數據庫中全部集合的索引總數的計數。 |
dbStats.indexSize | 在此數據庫上建立的全部索引的總大小 |
在密集(快達到數據庫最大容量)Collection建立索引:在默認狀況下,在密集的Collection(快達到數據庫最大容量)時建立索引,會阻止其餘操做。在給密集的Collection(快達到數據庫最大容量)建立索引時,
索引構建完成以前,保存Collection的數據庫不可用於讀取或寫入操做。 任何須要對全部數據庫(例如listDatabases)進行讀或寫鎖定的操做都將等待不是後臺進程的索引構建完成。
所以可使用background屬性進行設置後臺索引建立,操做以下:
db.people.createIndex( { zipcode: 1 }, { background: true } ) 默認狀況下,在建立索引時,background爲false,能夠和其餘屬性進行組合使用: db.people.createIndex( { zipcode: 1 }, { background: true, sparse: true } )
MongoDB能夠在任何一個字段中建立索引,默認狀況下,全部的集合(collections)會在_id字段中建立索引。_id索引是爲防止客戶端插入具備相同value的_id字段的文檔Document,並且不能刪除_id字段索引。
在分片羣集中使用_id索引,若是不使用_id字段做爲分片鍵,則應用程序必須確保_id字段中值的惟一性以防止出錯,解決方法爲使用標準的自動生成的ObjectId來完成。
通常單字段索引的value中,「1」指定按升序對項目進行排序的索引,「-1」指定按降序對項目進行排序的索引。以下所示:
在單個字段建立索引,示例以下:
{ "_id": ObjectId("570c04a4ad233577f97dc459"), "score": 1034, "location": { state: "NY", city: "New York" } } //建立單字段索引 db.records.createIndex( { score: 1 } ) //支持的查詢 db.records.find( { score: 2 } ) db.records.find( { score: { $gt: 10 } } )
在嵌入式文檔Document中的字段建立索引,示例以下:
db.records.createIndex( { "location.state": 1 } ) //支持的查詢 db.records.find( { "location.state": "CA" } ) db.records.find( { "location.city": "Albany", "location.state": "NY" } )
在嵌入式文檔Document建立索引,示例以下:
db.records.createIndex( { location: 1 } ) //支持查詢 db.records.find( { location: { city: "New York", state: "NY" } } )
複合索引指的是將多個key組合到一塊兒建立索引,這樣能夠加速匹配多個鍵的查詢。特性以下:
建立複合索引的格式:
db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )
排序順序,兩個字段的複合索引示例,index{userid:1,score:-1},先userid的value排序,而後再userid排序基礎下進行score排序。以下圖:
建立複合索引,示例以下:
{ "_id": ObjectId(...), "item": "Banana", "category": ["food", "produce", "grocery"], "location": "4th Street Store", "stock": 4, "type": "cases" } //建立複合索引 db.products.createIndex( { "item": 1, "stock": 1 } ) //支持的查詢 db.products.find( { item: "Banana" } ) db.products.find( { item: "Banana", stock: { $gt: 5 } } )
複合索引中的前綴查詢,示例以下:
//建立複合索引 db.products.createIndex({ "item": 1, "location": 1, "stock": 1 }) //前綴爲:{ item: 1 }與{ item: 1, location: 1 } //支持前綴查詢爲 db.products.find( { item: "Banana" } ) db.products.find( { item: "Banana", location: 「beijing」} ) //不支持前綴查詢,不會提升查詢效率 //不包含前綴字段 db.products.find( { location: 「beijing」} ) db.products.find( { stock: { $gt: 5 } ) db.products.find( { location: 「beijing」,stock: { $gt: 5 } ) //不按照建立複合索引字段順序的前綴查詢 db.products.find( { location: 「beijing」,item: "Banana" },stock: { $gt: 5 } )
MongoDB使用多鍵索引爲數組的每一個元素都建立索引,多鍵索引能夠創建在字符串、數字等key或者內嵌文檔(document)的數組上,若是索引字段包含數組值,MongoDB會自動肯定是否建立多鍵索引; 您不須要手動指定多鍵類型。 其中建立方式:
db.coll.createIndex( { <field>: < 1 or -1 > } )
索引邊界
使用多鍵索引,會出現索引邊界(索引邊界便是查詢過程當中索引能查找的範圍)的計算,並計算必須遵循一些規則。即當多個查詢的條件中字段都存在索引中時,MongoDB將會使用交集或者並集等來判斷這些條件索引字段的邊界最終產生一個最小的查找範圍。能夠分狀況:
1).交集邊界
交集邊界即爲多個邊界的邏輯交集,對於給定的數組字段,假定一個查詢使用了數組的多個條件字段而且可使用多鍵索引。若是使用了$elemMatch鏈接了條件字段,則MongoDB將會相交多鍵索引邊界,示例以下:
//survey Collection中document有一個item字段和一個ratings數組字段 { _id: 1, item: "ABC", ratings: [ 2, 9 ] } { _id: 2, item: "XYZ", ratings: [ 4, 3 ] } //在ratings數組上建立多鍵索引: db.survey.createIndex({ratings:1}) //兩種查詢 db.survey.find({ratings:{$elemMatch:{$gte:3,$lte:6}}}) //(1) db.survey.find( { ratings : { $gte: 3, $lte: 6 } } ) //(2)
查詢條件分別爲大於等於三、小於等於6,其中 (1)中使用了$elemMatch鏈接查詢條件,會產生一個交集ratings:[[3,6]。在(2)查詢中,沒使用$elemMatch,則不會產生交集,只要知足任何一個條件便可。
2).並集邊界
並集邊界經常用在肯定多鍵組合索引的邊界,例如:給定的組合索引{a:1,b:1},在字段a上有一個邊界:[3,+∞),在字段b上有一個邊界:(-∞,6],相併這兩個邊界的結果是:{ a: [ [ 3, Infinity ] ], b: [ [ -Infinity, 6 ] ] }。
並且若是MongoDB無法並集這兩個邊界,MongoDB將會強制使用索引的第一個字段的邊界來進行索引掃描,在這種狀況下就是: a: [ [ 3, Infinity ] ]。
三、數組字段的組合索引
一個組合索引的索引字段是數組,例如一個survey collection集合document文檔中含有item字段和ratings數組字段,示例以下:
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] } { _id: 2, item: "XYZ", ratings: [ 4, 3 ] } //在item字段和ratings字段建立一個組合索引: db.survey.createIndex( { item: 1, ratings: 1 } ) //查詢條件索引包含的兩個key db.survey.find( { item: "XYZ", ratings: { $gte: 3 } } )
分別處理查詢條件:
item: "XYZ" --> [ [ "XYZ", "XYZ" ] ]; ratings: { $gte: 3 } --> [ [ 3, Infinity ] ].
MongoDB使用並集邊界來組合這兩個邊界:
{ item: [ [ "XYZ", "XYZ" ] ], ratings: [ [ 3, Infinity ] ] }
4).內嵌文檔document的數組上創建組合索引
若是數組中包含內嵌文檔document,想在包含的內嵌文檔document字段上創建索引,須要在索引聲明中使用逗號「,」 來分隔字段名,示例以下:
ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ]
則score字段名稱就是:ratings.score。
5).混合不是數組類型的字段和數組類型字段的並集
{ _id: 1, item: "ABC", ratings: [ { score: 2, by: "mn" }, { score: 9, by: "anon" } ] } { _id: 2, item: "XYZ", ratings: [ { score: 5, by: "anon" }, { score: 7, by: "wv" } ] } //在item和數組字段ratings.score和ratings.by上建立一個組合索引 db.survey2.createIndex( { "item": 1, "ratings.score": 1, "ratings.by": 1 } ) //查詢 db.survey2.find( { item: "XYZ", "ratings.score": { $lte: 5 }, "ratings.by": "anon" } )
分別對查詢條件進行處理:
item: "XYZ"--> [ ["XYZ","XYZ"] ]; score: {$lte:5}--> [[-Infinity,5]]; by: "anon" -->["anon","anon"].
MongoDB能夠組合 item鍵的邊界與 ratings.score和ratings.by兩個邊界中的一個,究竟是score仍是by索引邊界這取決於查詢條件和索引鍵的值。MongoDB不能確保哪一個邊界和item字段進行並集。
但若是想組合ratings.score和ratings.by邊界,則查詢必須使用$elemMatch。
6).數組字段索引的並集邊界
在數組內部並集索引鍵的邊界,
好比:在ratings.score和ratings.by字段上建立組合索引:
db.survey2.createIndex( { "ratings.score": 1, "ratings.by": 1 } )
字段ratings.score和ratings.by擁有共同的路徑ratings。下面的查詢使用$elemMatch則要求ratings字段必須包含一個元素匹配這兩個條件:
db.survey2.find( { ratings: { $elemMatch: { score: { $lte: 5 }, by: "anon" } } } )
分別對查詢條件進行處理:
score: { $lte: 5 } --> [ -Infinity, 5 ]; by: "anon"--> [ "anon", "anon" ].
MongoDB可使用並集邊界來組合這兩個邊界:
{ "ratings.score" : [ [ -Infinity, 5 ] ], "ratings.by" : [ [ "anon", "anon" ] ] }
7). 還有不使用$elemMatch進行查詢以及不完整的路徑上使用$elemMatch,想要了解更多能夠查看《官方文檔-Multikey Index Bounds》。
限制:
MongoDB提供了一種全文索引類型,支持在Collection中搜索字符串內容,對字符串與字符串數組建立全文可搜索的索引
。 這些全文索引不存儲特定於語言的停用詞(例如「the」,「a」,「或」),而且阻止document集合中的單詞僅存儲根詞。建立方式以下:
db.collection.createIndex( { key: "text",key:"text" ..... } )
並且MongoDB提供權重以及通配符的建立方式。查詢方式多個字符串空格隔開,排除查詢使用「-」以下所示:
db.collection.find({$text:{$search:"runoob add -cc"}})
要刪除全本索引,須要將索引的名稱傳遞給db.collection.dropIndex()方法, 而要獲取索引的名稱,使用db.collection.getIndexes()方法。
還能夠指定全文索引的語言,經過default_language屬性 在建立時指定, 或者使用language_override屬性 覆蓋掉建立document文檔時默認的語言,以下所示:
//指定不一樣的語言的方法:建立全文索引的時候使用default_language屬性 db.collection.createIndex( { content : "text" }, { default_language: "spanish" }) //使用language_override屬性覆蓋默認的語言 db.quotes.createIndex( { quote : "text" }, { language_override: "idioma" } ) //默認的全文索引名稱爲context_text,users.comments.text,指定名稱MyTextIndex db.collection.createIndex( { content: "text", "users.comments": "text", "users.profiles": "text" }, { name: "MyTextIndex" } )
權重
每一個全文索引能夠經過設置權重來分配不一樣的搜索程度,默認權重爲1,對於文檔中的每一個索引字段,MongoDB將匹配數乘以權重並將結果相加。 使用此總和,MongoDB而後計算文檔的分數,示例以下:
{ _id: 1, content: "This morning I had a cup of coffee.", about: "beverage", keywords: [ "coffee" ] } { _id: 2, content: "Who doesn't like cake?", about: "food", keywords: [ "cake", "food", "dessert" ] } //經過db.blog.createIndex來指定weight權重 db.blog.createIndex( { content: "text", keywords: "text", about: "text" }, { weights: { content: 10, keywords: 5 }, name: "TextIndex" } )
content權重爲10,keywords爲5,about爲默認權重1,所以能夠得出content對於keywords查詢頻率高於2倍,而對於about字段則是10倍。
通配符全文索引
在多個字段上建立全文索引時,還可使用通配符說明符($**)。 使用通配符全文索引,MongoDB會爲包含Collection中每一個Document的字符串數據。例如:
db.collection.createIndex( { "$**": "text" } )
通配符全本索引是多個字段上的全本索引。 所以,能夠在建立索引期間爲特定字段指定權重,以控制結果的排名。
限制
散列索引使用散列函數來計算索引字段值的散列值。 散列函數會摺疊嵌入的文檔並計算整個值的散列值,但不支持多鍵(即數組)索引。
生成hash索引key使用了convertShardKeyToHashed()方法。建立方式以下:
db.collection.createIndex( { _id: "hashed" } )
並且散列索引支持使用散列分片鍵進行分片。 基於散列的分片使用字段的散列索引做爲分片鍵來分割整個分片羣集中的數據。
索引屬性有TTL索引、唯一性索引、部分索引、稀疏索引以及區分大小寫索引。
TTL索引是特殊的單字段索引,而且字段類型必須是date類型或者包含有date類型的數組,MongoDB可使用它在必定時間後或在特定時鐘時間自動從集合中刪除文檔。 數據到期對於某些類型的信息很是有用,例如機器生成的事件數據,日誌和會話信息,這些信息只須要在數據庫中持續有限的時間。
建立TTL索引方法,和普通索引的建立方法同樣,只是會多加一個expireAfterSeconds的屬性,格式以下:
db.collection.createIndex( {key and index type specification},{ expireAfterSeconds: time})
例子:
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
指定過時時間
首先在保存BSON日期類型值或BSON日期類型對象數組的字段上建立TTL索引,並指定expireAfterSeconds值爲0.對於集合中的每一個文檔,設置 索引日期字段爲與文檔到期時間對應的值。示例操做以下:
第一步:
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
第二步:
db.log_events.insert( { "expireAt": new Date('July 22, 2013 14:00:00'), "logEvent": 2, "logMessage": "Success!" } )
數據過時類型:
TTL索引特有限制:
惟一索引可確保索引字段不存儲重複值; 即強制索引字段的惟一性。 默認狀況下,MongoDB在建立集合期間在_id字段上建立惟一索引。建立方式以下:
db.collection.createIndex( <key and index type specification>, { unique: true } )
單個字段建立方式,示例以下:
db.members.createIndex( { "user_id": 1 }, { unique: true } )
惟一性複合索引: 還能夠對複合索引強制執行惟一約束。 若是對複合索引使用惟一約束,則MongoDB將對索引鍵值的組合強制實施惟一性。示例以下:
//建立的索引且強制groupNumber,lastname和firstname值組合的惟一性。 db.members.createIndex( { groupNumber: 1, lastname: 1, firstname: 1 }, { unique: true } )
惟一多鍵索引:
{ _id: 1, a: [ { loc: "A", qty: 5 }, { qty: 10 } ] } //建立索引: db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } ) //插入數據:惟一索引容許將如下Document插入Collection中,由於索引強制執行a.loc和a.qty值組合的惟一性: db.collection.insert( { _id: 2, a: [ { loc: "A" }, { qty: 5 } ] } ) db.collection.insert( { _id: 3, a: [ { loc: "A", qty: 10 } ] } )
建立惟一索引到副本或者分片中:
對於副本集和分片集羣,使用滾動過程建立惟一索引須要在過程當中中止對集合的全部寫入。 若是在過程當中沒法中止對集合的全部寫入,請不要使用滾動過程。 相反,經過如下方式在集合上構建惟一索引:
NOTE:詳細解析能夠看
限制:
部分索引經過指定的過濾表達式去達到局部搜索。經過db.collection.createIndex()方法中增長partialFilterExpression屬性建立,過濾表達式以下:
示例以下:
//建立部分索引 db.restaurants.createIndex( { cuisine: 1, name: 1 }, { partialFilterExpression: { rating: { $gt: 5 } } } ) //查詢狀況分類 db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } ) //(1) db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } ) //(2) db.restaurants.find( { cuisine: "Italian" } ) //(3)
其中:
限制:
稀疏索只引搜索包含有索引字段的文檔的條目,跳過索引鍵不存在的文檔,即稀疏索引不會搜索不包含稀疏索引的文檔。默認狀況下, 2dsphere (version 2), 2d, geoHaystack, 全文索引等老是稀疏索引。建立方式db.collection.createIndex()方法增長sparse屬性,以下所示:
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
稀疏索引不被使用的狀況: 若是稀疏索引會致使查詢和排序操做的結果集不完整,MongoDB將不會使用該索引,除非hint()示顯式指定索引。
稀疏複合索引:
稀疏索引與惟一性: 一個既包含稀疏又包含惟一的索引避免集合上存在一些重複值得文檔,可是容許多個文檔忽略該鍵。知足稀疏索引和惟一性操做其兩個限制都要遵循。
整合示例以下:
{ "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" } { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 } { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 } //在score中建立稀疏索引: db.scores.createIndex( { score: 1 } , { sparse: true } ) //查詢 db.scores.find( { score: { $lt: 90 } } ) //(1) db.scores.find().sort( { score: -1 } ) //(2) db.scores.find().sort( { score: -1 } ).hint( { score: 1 } ) //(3) //在score字段上建立具備惟一約束和稀疏過濾器的索引: db.scores.createIndex( { score: 1 } , { sparse: true, unique: true } ) //該索引容許插入具備score字段的惟一值的文檔或不包括得分字段的文檔。以下: db.scores.insert( { "userid": "AAAAAAA", "score": 43 } ) db.scores.insert( { "userid": "BBBBBBB", "score": 34 } ) db.scores.insert( { "userid": "CCCCCCC" } ) db.scores.insert( { "userid": "DDDDDDD" } ) //索引不容許添加如下文檔,由於已存在score值爲82和90的文檔 db.scores.insert( { "userid": "AAAAAAA", "score": 82 } ) db.scores.insert( { "userid": "BBBBBBB", "score": 90 } )
其中:
索引策略:
後續還會有MongonDB索引優化,副本集以及分片總結、最重要還會總結在使用MongoDB實戰以及實戰過程出現的一些坑,可關注後續更新MongoDB系列。
最後可關注公衆號[Ccww筆記],一塊兒學習,天天會分享乾貨,還有學習視頻乾貨領取!