MongoDB系列--輕鬆應對面試中遇到的MongonDB索引(index)問題

  索引是特殊的數據結構,索引存儲在一個易於遍歷讀取的數據集合中( 索引存儲在特定字段或字段集的值),並且是使用了B-tree結構。索引能夠極大程度提高MongoDB查詢效率。
  若是沒有索引,MongoDB必須執行全集合collections掃描,即掃描集合中的每一個文檔,選取符合查詢條件的文檔document。 若是查詢時存在適當的索引,MongoDB可使用索引來限制它必須查詢的文檔document的數量,特別是在處理大量數據時,因此選擇正確的索引是很關鍵的、重要的mongodb

建立索引,須要考慮的問題:數據庫

  • 每一個索引至少須要數據空間爲8kb;
  • 添加索引會對寫入操做會產生一些性能影響。 對於具備高寫入率的集合Collections,索引很昂貴,由於每一個插入也必須更新任何索引;
  • 索引對於具備高讀取率的集合Collections頗有利,不會影響沒索引查詢;
  • 處於索引處於action狀態時,每一個索引都會佔用磁盤空間和內存,所以須要對這種狀況進行跟蹤檢測。

索引限制:數組

  • 索引名稱長度不能超過128字段;
  • 複合索引不能超過32個屬性;
  • 每一個集合Collection不能超過64個索引;
  • 不一樣類型索引還具備各自的限制條件。

1. 索引管理

1.1 索引建立

索引建立使用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.

1.2 查看索引

查看Collection中全部索引,格式以下:ide

db.collection.getIndexes()

1.3 刪除索引

刪除Collection中的索引:格式以下:函數

db.collection.dropIndexes()   //刪除全部索引
db.collection.dropIndex()    //刪除指定的索引

1.4 索引名稱

索引的默認名稱是索引鍵和索引中每一個鍵的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

1.5 查看索引建立過程以及終止索引建立

方法 解析
db.currentOp() 查看索引建立過程
db.killOp(opid) 終止索引建立,其中-opid爲操做id

1.6 索引使用狀況

形式 解析
&dollar;indexStats 獲取索引訪問信息
explain() 返回查詢狀況:在executionStats模式下使用db.collection.explain()或cursor.explain()方法返回有關查詢過程的統計信息,包括使用的索引,掃描的文檔數以及查詢處理的時間(以毫秒爲單位)。
Hint() 控制索引,例如要強制MongoDB使用特定索引進行db.collection.find()操做,請使用hint()方法指定索引

1.7 MongoDB度量標準

MongoDB提供了許多索引使用和操做的度量標準,在分析數據庫的索引使用時可能須要考慮這些度量標準,以下所示:優化

形式 解析
metrics.queryExecutor.scanned 在查詢和查詢計劃評估期間掃描的索引項的總數
metrics.operation.scanAndOrder 返回沒法使用索引執行排序操做的已排序數字的查詢總數
collStats.totalIndexSize 全部索引的總大小。 scale參數會影響此值。若是索引使用前綴壓縮(這是WiredTiger的默認值),則返回的大小將反映計算總計時任何此類索引的壓縮大小。
collStats.indexSizes 指定集合collection上每一個現有索引的鍵和大小。 scale參數會影響此值
dbStats.indexes 包含數據庫中全部集合的索引總數的計數。
dbStats.indexSize 在此數據庫上建立的全部索引的總大小

1.8 後臺索引操做

  在密集(快達到數據庫最大容量)Collection建立索引:在默認狀況下,在密集的Collection(快達到數據庫最大容量)時建立索引,會阻止其餘操做。在給密集的Collection(快達到數據庫最大容量)建立索引時,
索引構建完成以前,保存Collection的數據庫不可用於讀取或寫入操做。 任何須要對全部數據庫(例如listDatabases)進行讀或寫鎖定的操做都將等待不是後臺進程的索引構建完成。

所以可使用background屬性進行設置後臺索引建立,操做以下:

db.people.createIndex( { zipcode: 1 }, { background: true } )
默認狀況下,在建立索引時,background爲false,能夠和其餘屬性進行組合使用:
db.people.createIndex( { zipcode: 1 }, { background: true, sparse: true } )

2. 索引類型

2.1 單字段索引(Single Field Indexes)

  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" } } )

2.2 複合索引(Compound Index)

複合索引指的是將多個key組合到一塊兒建立索引,這樣能夠加速匹配多個鍵的查詢。特性以下:

  • MongoDB對任何複合索引都限制了32個字段;
  • 沒法建立具備散列索引(hash index)類型的複合索引。若是嘗試建立包含散列索引字段的複合索引,則會報錯;
  • 複合索引建立字段索引的順序是很重要的。由於索引以升序(1)或降序(-1)排序順序存儲對字段的引用; 對於單字段索引,鍵的排序順序可有可無,由於MongoDB能夠在任一方向上遍歷索引。 可是,對於複合索引,排序順序能夠決定索引是否能夠支持排序操做;
  • 除了支持在全部索引字段上匹配的查詢以外,複合索引還能夠支持與索引字段的前綴匹配的查詢。

建立複合索引的格式:

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 } )

2.3 多鍵索引

  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)中使用了&dollar;elemMatch鏈接查詢條件,會產生一個交集ratings:[[3,6]。在(2)查詢中,沒使用&dollar;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邊界,則查詢必須使用&dollar;elemMatch

6).數組字段索引的並集邊界
  在數組內部並集索引鍵的邊界,

  • 除了字段名稱外,索引鍵必須有相同的字段路徑,
  • 查詢的時候必須在路徑上使用$elemMatch進行聲明
  • 對於內嵌的文檔,使用逗號分隔的路徑,好比a.b.c.d是字段d的路徑。爲了在相同的數組上並集索引鍵的邊界,須要$elemMatch必須使用在a.b.c的路徑上。

好比:在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). 還有不使用&dollar;elemMatch進行查詢以及不完整的路徑上使用&dollar;elemMatch,想要了解更多能夠查看《官方文檔-Multikey Index Bounds》。

限制:

  • 對於一個組合多鍵索引,每一個索引文檔最多隻能有一個索引字段的值是數組。若是組合多鍵索引已經存在了,不能在插入文檔的時候違反這個限制;
  • 不能聲明一個多鍵索引做爲分片鍵索引;
  • 哈希索引不能擁有多鍵索引;
  • 多鍵索引不能進行覆蓋查詢;
  • 當一個查詢聲明把數組總體做爲精確匹配的時候,MongoDB可使用多鍵索引來查找這個查詢數組的第一個元素,可是不能使用多鍵索引掃描來找出整個數組。代替方案是當使用多鍵索引查詢出數組的第一個元素以後,MongoDB再對過濾以後的文檔再進行一次數組匹配。

2.4 全文索引(text index)

  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" } )

通配符全本索引是多個字段上的全本索引。 所以,能夠在建立索引期間爲特定字段指定權重,以控制結果的排名。

限制

  • 每一個Collection一個全文索引:一個collection最多隻有一個全文索引,
  • Text Search 和Hints函數,若是查詢包含$ text查詢表達式,則不能使用hint();
  • Text Index and Sort,排序操做沒法從文本索引獲取排序順序,即便是複合文本索引也是如此; 即排序操做不能使用文本索引中的排序;
  • **複合索引:複合索引能夠包括文本索引鍵與升序/降序索引鍵的組合。 可是,這些複合索引具備如下限制:
    1).複合文本索引不能包含任何其餘特殊索引類型,例如多鍵或地理空間索引字段。
    2).若是複合文本索引包括文本索引鍵以前的鍵,則執行$ text搜索時,查詢謂詞必須包含前面鍵上的相等匹配條件。
    3).建立複合文本索引時,必須在索引規範文檔中相鄰地列出全部文本索引鍵。**

2.5 Hash 索引

  散列索引使用散列函數來計算索引字段值的散列值。 散列函數會摺疊嵌入的文檔並計算整個值的散列值,但不支持多鍵(即數組)索引。
生成hash索引key使用了convertShardKeyToHashed()方法。建立方式以下:

db.collection.createIndex( { _id: "hashed" } )

並且散列索引支持使用散列分片鍵進行分片。 基於散列的分片使用字段的散列索引做爲分片鍵來分割整個分片羣集中的數據。

3. 索引屬性

索引屬性有TTL索引、唯一性索引、部分索引、稀疏索引以及區分大小寫索引。

3.1 TTL索引(TTL Indexes)

  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!"
} )

數據過時類型:

  • 當指定時間到了過時的閾值數據就會過時並刪除;
  • 若是字段是數組,而且索引中有多個日期值,MongoDB使用數組中最低(即最先)的日期值來計算到期閾值;
  • 若是文檔(document)中的索引字段不是日期或包含日期值的數組,則文檔(document)將不會過時;
  • 若是文檔(document)不包含索引字段,則文檔(document)不會過時。

TTL索引特有限制:

  • TTL索引是單字段索引。 複合索引不支持TTL並忽略expireAfterSeconds選項;
  • _id屬性不支持TTL索引;
  • 沒法在上限集合上建立TTL索引,由於MongoDB沒法從上限集合中刪除文檔;
  • 不能使用createIndex()方法來更改現有索引的expireAfterSeconds值。而是將collMod數據庫命令與索引集合標誌結合使用。 不然,要更改現有索引的選項的值,必須先刪除索引並從新建立;
  • 若是字段已存在非TTL單字段索引,則沒法在同一字段上建立TTL索引,由於沒法在相同key建立不一樣類型的的索引。 要將非TTL單字段索引更改成TTL索引,必須先刪除索引並使用expireAfterSeconds選項從新建立。

3.2 唯一性索引(Unique Indexes)

  惟一索引可確保索引字段不存儲重複值; 即強制索引字段的惟一性。 默認狀況下,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 } ] } )

建立惟一索引到副本或者分片中:
對於副本集和分片集羣,使用滾動過程建立惟一索引須要在過程當中中止對集合的全部寫入。 若是在過程當中沒法中止對集合的全部寫入,請不要使用滾動過程。 相反,經過如下方式在集合上構建惟一索引:

  • 在主服務器上爲副本集發出db.collection.createIndex()
  • 在mongos上爲分片羣集發出db.collection.createIndex()

NOTE:詳細解析能夠看

限制:

  • 若是集合已經包含超出索引的惟一約束的數據(即有重複數據),則MongoDB沒法在指定的索引字段上建立惟一索引。
  • 不能在hash索引上建立惟一索引
  • 惟一約束適用於Collection中的一個Document。因爲約束適用於單文檔document,所以對於惟一的多鍵索引,只要該文檔document的索引鍵值不與另外一個文檔document的索引鍵值重複,文檔就可能具備致使重複索引鍵值的數組元素。 在這種狀況下,重複索引記錄僅插入索引一次。
  • **分片Collection惟一索引只能以下:
    1).分片鍵上的索引
    2).分片鍵是前綴的複合索引
    3). 默認的_id索引; 可是,若是_id字段不是分片鍵或分片鍵的前綴,則_id索引僅對每一個分片強制執行惟一性約束。若是_id字段不是分片鍵,也不是分片鍵的前綴,MongoDB但願應用程序在分片中強制執行_id值的惟一性。**

3.3 部分索引(Partial Indexes)

  部分索引經過指定的過濾表達式去達到局部搜索。經過db.collection.createIndex()方法中增長partialFilterExpression屬性建立,過濾表達式以下:

  • 等式表達式(即 file:value或使用&dollar;eq運算符)
  • &dollar;exists表達式
  • &dollar;gt,&dollar;gte,&dollar;lt,&dollar;lte 表達式
  • &dollar;type表達式
  • &dollar;and

示例以下

//建立部分索引
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)

其中:

  • (1)查詢: 查詢條件{ &dollar;gte: 8 }於建立索引條件{ &dollar;gt: 5 }能夠構成一個完整集(查詢條件是建立索引條件的子集,即大於5能夠包含大於等於 8),可使用部分索引查詢。
  • (2)查詢: 條件達不到完整集,MongoDB將不會將部分索引用於查詢或排序操做。
  • (3)查詢: 次查詢沒有使用過濾表達式,也不會使用部分索引,由於要使用部分索引,查詢必須包含過濾器表達式(或指定過濾器表達式子集的已修改過濾器表達式)做爲其查詢條件的一部分

限制:

  • 不能夠僅經過過濾表達式建立多個局部索引;
  • 不能夠同時使用局部索引和稀疏索引(sparse index);
  • _id索引不能使用局部索引,分片索引(shard key index)也不能使用局部索引;
  • 同時指定partialFilterExpression和惟一約束,則惟一約束僅適用於知足過濾器表達式的文檔。 若是Document不符合篩選條件,則具備惟一約束的部分索引是容許插入不符合惟一約束的Document。

3.4 稀疏索引(Sparse Indexes)

  稀疏索只引搜索包含有索引字段的文檔的條目,跳過索引鍵不存在的文檔,即稀疏索引不會搜索不包含稀疏索引的文檔。默認狀況下, 2dsphere (version 2), 2d, geoHaystack, 全文索引等老是稀疏索引。建立方式db.collection.createIndex()方法增長sparse屬性,以下所示:

db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )

稀疏索引不被使用的狀況: 若是稀疏索引會致使查詢和排序操做的結果集不完整,MongoDB將不會使用該索引,除非hint()示顯式指定索引。

稀疏複合索引:

  • 對於包含上升/降低排序的稀疏複合索引,只要複合索引中的一個key 索引存在都會被檢測出來
  • 對於包含上升/降低排序的包含地理空間能夠的稀疏複合索引,只有存在地理空間key才能被檢測出來
  • 對於包含上升/降低排序的全文索引的稀疏複合索引,只有存在全文索引索引才能夠被檢測

稀疏索引與惟一性: 一個既包含稀疏又包含惟一的索引避免集合上存在一些重複值得文檔,可是容許多個文檔忽略該鍵。知足稀疏索引和惟一性操做其兩個限制都要遵循。

整合示例以下:

{ "_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 } )

其中:

  • (1)查詢: 可使用稀疏索引查詢,返回完整集:{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
  • (2)查詢: 即便排序是經過索引字段進行的,MongoDB也不會選擇稀疏索引來完成查詢以返回完整的結果:
    **{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
    { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
    { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" }**
  • (3)查詢: 使用hint()返回所需完整集:
    **{ "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }
    { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }**

4. 其餘事項

4.1 索引策略

索引策略:

  • 應用程序的最佳索引必須考慮許多因素,包括指望查詢的類型,讀取與寫入的比率以及系統上的可用內存量。
  • 在開發索引策略時,您應該深刻了解應用程序的查詢。在構建索引以前,映射將要運行的查詢類型,以便您能夠構建引用這些字段的索引。索引具備性能成本,可是對於大型數據集上的頻繁查詢而言,它們的價值更高。考慮應用程序中每一個查詢的相對頻率以及查詢是否證實索引是合理的。
  • 設計索引的最佳整體策略是使用與您將在生產中運行的數據集相似的數據集來分析各類索引配置,以查看哪些配置性能最佳。檢查爲集合建立的當前索引,以確保它們支持您當前和計劃的查詢。若是再也不使用索引,請刪除索引。
  • 一般,MongoDB僅使用一個索引來完成大多數查詢。可是,$或查詢的每一個子句可能使用不一樣的索引,從2.6開始,MongoDB可使用多個索引的交集。

4.2 後續

  後續還會有MongonDB索引優化,副本集以及分片總結、最重要還會總結在使用MongoDB實戰以及實戰過程出現的一些坑,可關注後續更新MongoDB系列。

最後可關注公衆號[Ccww筆記],一塊兒學習,天天會分享乾貨,還有學習視頻乾貨領取!

相關文章
相關標籤/搜索