在1個鍵上創建的索引就是單鍵索引,單鍵索引是最多見的索引,如MongoDB默許創建的_id的索引就是單鍵索引。
在多個鍵上建立的索引就是復合索引
如果在1個值為數組的字段上面創建索引, MongoDB會自己決定,是不是要把這個索引建成多鍵索引
MongoDB支持幾種類型的地理空間索引。其中最經常使用的是 2dsphere 索引(用于地球表面類型的地圖)和 2d 索引(用于平面地圖和時間連續的數據)
全文索援用于在文檔中搜索文本,我們也能夠使用正則表達式來查詢字符串,但是當文本塊比較大的時候,正則表達式搜索會非常慢,而且沒法處理語言理解的問題(如 entry 和 entries 應當算是匹配的)。使用全文索引可以非常快地進行文本搜索,就猶如內置了多種語言分詞機制的支持1樣。創建索引的開消都比較大,全文索引的開消更大。創建索引時,需后臺或離線創建
哈希 索引可以支持相等查詢,但是 哈希 索引不支持范圍查詢。您可能沒法創建1個帶有 哈希 索引鍵的復合索引或對 哈希 索引施加唯1性的限制。但是,您可以在同1個鍵上同時創建1個 哈希 索引和1個遞增/遞減(例如,非哈希)的索引,這樣MongoDB對范圍查詢就會自動使用非哈希的索引
- 唯1索引可以謝絕保存那些被索引鍵的值已重復的文檔。
- 默許情況下,MongoDB索引的 unique 屬性是 false 。如果對復合索引施加唯1性的限制,那末MongoDB就會強迫要求 復合 值的唯1性,而不是分別對每一個單獨的值要求唯1。
- 唯1性的限制是針對1個集合中不同文檔的。也即,唯1索引可以避免 不同 文檔的被索引鍵上存儲相同值,但是它不由止同1篇文檔在被索引鍵存儲的數組里存儲的元素或內嵌文檔是相同的值。
- 在同1篇文檔存儲重復數據的情況下,重復的值只會被存入索引1次。
- 如果1篇文檔不包括唯1索引的被索引鍵,那末索引默許會為該文檔存儲1個null值。由于唯1性的限制,MongoDB將只允許有1篇可以不包括被索引鍵。如果超過1篇文檔不包括被索引鍵或沒有值,那末會拋出鍵重復(duplicate key)毛病致使索引創建失敗。可以組合使用唯1性和稀疏索引的特性來過濾那些包括null值的文檔以免這個毛病。
- 稀疏索引會跳過所有不包括被索引鍵的文檔。這個索引之所以稱為 “稀疏” 是由于它其實不包括集合中的所有文檔。與之相反,非稀疏的索引會索引每篇文檔,如果1篇文檔不含被索引鍵則為它存儲1個null值。
- 如果1個索引會致使查詢或排序的結果集是不完全的,那末MongoDB將不會使用這個索引,除非用戶使用 hint() 方法來顯示指定索引。例如,查詢 { x: { $exists: false } } 將不會使用 x 鍵上的稀疏索引,除非顯示的hint。
- 2dsphere (version 2), 2d 和 text 這些索引總是稀疏的。
- 只要1個文檔里有最少1個被索引鍵,稀疏且只包括有遞增/遞減索引鍵的復合索引就會索引這篇文檔。
- 至于稀疏且包括有地理索引鍵(例如 2dsphere, 2d)和遞增/遞減索引鍵的復合索引,只有地理索引鍵的存在與否能決定1篇文檔是不是被索引。
- 至于稀疏且包括了全文索引鍵和其他遞增/遞減索引鍵的復合索引,只有全文索引鍵的存在與否能決定是不是索引該文檔。
- 1個稀疏且唯1的索引,可以避免集合中的文檔被索引鍵中出現重復值,同時也允許多個文檔里不包括被索引鍵
備注:3.2.0 版本后推薦使用Partial Indexes
- 這是3.2.0版本以后新增的
- 只會對Collections滿足條件partialFilterExpression的文檔進行索引
- 使用該索引,會在1定程度上減少存儲空間和創建索引和保護性能下降的本錢
- TTL 集合支持失效時間設置,當超過指定時間后,集合自動清除超時的文檔,這用來保存1些諸如session會話信息的時候非常有用,或存儲緩存數據使用。但刪除會有延時
- 索引的字段必須是1個日期的 bson 類型
- 你不能創建 TTL 索引,如果要索引的字段已在其他索引中使用。否則超時后文檔不會被自動清除。
- 索引不能包括多個字段
參數名 | 類型 | 描寫 |
---|---|---|
background | Boolean | 建索引進程會阻塞其它數據庫操作,background可指定以后臺方式創建索引,即增加 “background” 可選參數。 “background” 默許值為false。 |
unique | Boolean | 建立的索引是不是唯1。指定為true創建唯1索引。默許值為false. |
name | string | 索引的名稱。如果未指定,MongoDB的通過連接索引的字段名和排序順序生成1個索引名稱。不能超過128字符 |
partialFilterExpression | Document | 設定索引只對滿足條件的文檔起作用 具體見官網對Partial Indexes |
sparse | Boolean | 對文檔中不存在的字段數據不啟用索引;這個參數需要特別注意,如果設置為true的話,在索引字段中不會查詢出不包括對應字段的文檔.。默許值為 false. |
expireAfterSeconds | integer | 指定1個以秒為單位的數值,完成 TTL設定,設定集合的生存時間。 |
storageEngine | Document | 允許用戶在創建索引時指定每個索引的配置 |
參數名 | 類型 | 描寫 |
---|---|---|
weights | document | 設置文本索引字段的權重,權重值1- 99,999 |
default_language | 設置文本分詞的語言,默許為english,其他支持語言 | |
language_override | string | 使用文檔中的1個字段的值作為設置文本分詞的語言,默許為language,例子 |
textIndexVersion | integer | 版本號,可以是1或2 |
備注:其他索引屬性見官網
語法:db.collection.getIndexes()
示例:
db.index_test.getIndexes()
輸出:
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "usercenter_test.index_test"
}
]
可以看出_id字段是默許建立了索引的
從3.0版本后使用 db.collection.createIndex()代替db.collection.ensureIndex()
語法:db.collection.createIndex(keys, options)
參數說明:
1. keys: {字段名1:ascending,… 字段名n:ascending}: ascending 設為1 標識索引升序,⑴降序
2. options : 設置索引選項,如設置名稱、設置成為唯1索引
準備數據
db.index_test.insert({"name":"2","age":53,"sex":1})
db.index_test.insert({"name":"3","age":19,"sex":0})
db.index_test.insert({"name":"4","age":20,"sex":2})
db.index_test.insert({"name":"5","age":63,"sex":5})
db.index_test.insert({"name":"6","age":18,"sex":0})
db.index_test.insert({"name":"7","age":98,"sex":1})
db.index_test.insert({"name":"8","age":76,"sex":1})
db.index_test.insert({"name":"9","age":7,"sex":2})
db.index_test.insert({"name":"l0","age":15,"sex":1})
db.index_test.insert({"name":"l","age":23,"sex":1})
語法: db.collections.createIndex({“字段名”:1或⑴},{options})
示例:
db.index_test.createIndex({"age":1})
履行成功后可以看到返回的numIndexesAfter比numIndexesBefore大
示例1:查詢字段不包括索引字段
db.index_test.find({"sex":1}).explain("executionStats")
可以看到其 winningPlan.stage=COLLSCAN是全表掃描
示例2:查詢字段同時包括索引字段和非索引字段
db.index_test.find({"age":{"$gte":10},"sex":1}).explain("executionStats")
雖然 winningPlan.stage=FETCH和winningPlan.inputStage.stage =IXSCAN,但是其totalKeysExamined和totalDocsExamined都比nReturned大,說明在查詢的時候進行了1些沒有必要的掃描。
示例3:查詢字段同時只包括索引字段
db.index_test.find({"age":{"$gte":10}}).explain("executionStats")
可以看到返回中的
winningPlan.stage=FETCH(根據索引去檢索指定document )
winningPlan.inputStage.stage =IXSCAN(索引掃描)
executionStats.nReturned=totalKeysExamined=totalDocsExamined=9表示該查詢使用的根據索引去查詢指定文檔
(nReturned:查詢返回的條目,totalKeysExamined:索引掃描條目,totalDocsExamined:文檔掃描條目)
總結:在設置查詢字段時應盡可能只設置建立了索引的字段
示例1:排序字段不包括索引字段
db.index_test.find({"age":20}).sort({"sex":-1}).explain()
返回中的winningPlan.stage=SORT 即查詢后需要在內存中排序再返回
示例2:排序字段同時包括索引字段和非索引字段
db.index_test.find({"age":20}).sort({"age":1,"sex":1}).explain()
結果與上面1樣
示例3:排序字段只包括1個單個索引字段
db.index_test.find({"age":20}).sort({"age":1}).explain()
可以看到winningPlan.stage變成了FETCH(使用索引)
示例4:排序字段包括多個單個索引字段
db.index_test.find({}).sort({"sex":1,"age":1}).explain("executionStats")
db.index_test.find({}).sort({"age":1,"sex":1).explain("executionStats")
可以看到這類情況winningPlan.stage為sort即索引在排序中沒有起到作用,這說明單鍵索引在多個字段排序時沒有作用。
總結:
- 排序操作可以通過從索引中依照索引順序獲得文檔的方式來保證結果的有序性。如果查詢計劃器(planner)沒法從索引中得到排序順序,那末它將需要在內存中排序(winningPlan.stage=SORT)結果。
- 在多個字段上做排序時需要使用復合索引
語法: db.collections.createIndex({“字段名1”:1或⑴,…,”字段名n”:1或⑴},{options})
示例:
db.index_test.dropIndexes() //先刪除原來創建的索引
db.index_test.createIndex({"age":1,"sex":1}) //在age和sex上創建復合索引
創建成功后,通過db.index_test.getIndexes() 可看到創建的復合索引其實只是1個索引,其key是{“age” : 1,”sex” : 1};而不是多個。
示例1:查詢字段只包括創建復合索引字段中的部份字段
db.index_test.dropIndexes() //先刪除原來創建的索引
db.index_test.createIndex({"age":1,"sex":1}) //在age和sex上創建復合索引
然后分別履行
db.index_test.find({"age":1}).explain()
db.index_test.find({"sex":1}).explain()
可以看到第1句履行返回的winningPlan.stage=FETCH;且winningPlan.inputStage.stage=IXSCAN;indexName=age_⑴_sex_1
而第2句履行返回的wininigPlan.stage=COLLSCAN
這好像說明查詢條件只是復合索引key中部份字段時,索引只對創建時指定的第1個字段有作用。接下來我們先刪除原本的索引,然后在age,name,sex3個字段上創建1個復合索引再來看看。
db.index_test.dropIndexes()
db.index_test.createIndex({"age":-1,"name":1,"sex":1})
db.index_test.find({"age":"1"}).explain() //第1條查詢
db.index_test.find({"sex":"1"}).explain() //第2條查詢
db.index_test.find({"age":"1","sex":1}).explain()//第3條查詢
db.index_test.find({"name":"1","sex":1}).explain()//第4條查詢
可以看到第1條和第3條查詢返回的都是winningPlan.stage=FETCH;而第2條和第4條查詢返回的都是winningPlan.stage=COLLSCAN
總結:
查詢條件只是復合索引key中部份字段時,如果想要復合索引發到優化的作用則必須包括創建復合索引時指定的第1個字段;如上面的示列中的字段”age”
示例1:排序字段只包括創建復合索引字段中的部份字段
db.index_test.find().sort({"age":-1}).explain()
db.index_test.find().sort({"age":1}).explain()
db.index_test.find().sort({"name":1}).explain()
db.index_test.find().sort({"sex":1}).explain()
db.index_test.find().sort({"age":-1,"name":1}).explain()
db.index_test.find().sort({"name":1,"sex":1}).explain()
db.index_test.find().sort({"age":-1,"sex":1}).explain()
上面的只有第1,2,5 返回的winningPlan.stage= FETCH ;其他都是=sort
這說明排序字段只包括創建復合索引字段中的部份字段時排序鍵的順序必須和它們在索引中的排列順序 1致,且不能跳躍(即第1個字段必須有,且不能跳過中間的字段)接下來再看下面的查詢情況
db.index_test.find().sort({"age":1,"name":-1}).explain()
db.index_test.find().sort({"age":-1,"name":-1}).explain()
第1條返回的winningPlan.stage= FETCH;而第2條winningPlan.stage= SORT;這說明sort中指定的所有鍵的排序順序(例如遞增/遞減)必須和索引中的對應鍵的排序順序 完全相同, 或 完全相反
總結
可以指定在索引的所有鍵或部份鍵上排序。但是,排序鍵的順序必須和它們在索引中的排列順序 1致
sort中指定的所有鍵的排序順序(例如遞增/遞減)必須和索引中的對應鍵的排序順序 完全相同, 或 完全相反
語法:db.collections.createIndex({“字段名”: 1或⑴},{“unique”:true})
示例1:
db.index_test.createIndex({"name":1},{"unique":true})
db.index_test.createIndex({"sex":1},{"unique":true})
可以看到第1條履行成功,而第2條則失敗由于已存在的數據在sex字段上有重復的數據。
示例2:
db.index_test.insert({"name":"2","age":123,"sex":9})
db.index_test.insert({"name":"22","age":123,"sex":9})
可以看到第1條履行失敗,第2條成功。由于唯1索引會禁止寫入在唯1索引字段上重復的數據
總結1:
唯1索引會禁止利用插入被索引鍵上的值是重復值的 documents
不能再已有重復數據的字段上建立唯1索引
語法:db.collections.createIndex({“字段名”: 1或⑴,…”字段名n”: 1或⑴},{“unique”:true})
db.index_test.createIndex({"name":1,"age":1},{"unique":true}
db.index_test.insert({"name":"22","age":123,"sex":1})
db.index_test.insert({"name":"23","age":123,"sex":1})
db.index_test.insert({"name":"22","age":123,"sex":2})
可以看到第2條和第3條都能成功,但最后1條卻是失敗的。這說明在多字段上建唯1索引會強迫要求 復合 鍵值的唯1性,而 不是 每一個鍵的唯1性。
總結:
- 唯1索引會禁止利用插入被索引鍵上的值是重復值的 documents
- 不能再已有重復數據的字段上建立唯1索引
- 在多字段上建唯1索引會強迫要求 復合 鍵值的唯1性,而 不是 每一個鍵的唯1性
語法: db.collection.createIndex({“字段名”:1},{“sparse”:true})
在1個字段上創建稀疏索引,索引會跳過所有不包括被索引鍵(字段)的文檔
在履行查詢 { 被索引鍵:{$exists:false}},不會使用該稀疏索引,除非顯示指定hint({被索引鍵:1})
示例:
db.scores.insert({"userid":"lxh"})
db.scores.insert({"userid":"pen","score":89})
db.scores.insert({"userid":"xiao","score":90})
//在字段score上創建稀疏索引
db.scores.createIndex( { score: 1 } , {sparse:true} )
db.scores.find( { score:{$exists:false}}).explain()
db.scores.find( { score:{$exists:false}}).hint({score:1}).explain()
db.scores.find( { score:{$exists:true}}).explain()
從例子可以看出第5條在履行$exists:false 查詢時,其返回的winningPlan.stage=COLLSCAN(表示掃描全表);
第5條履行exists:false 查詢時,顯示指定了hint,其返回的winningPlan.stage=FETCH(使用索引掃描);
最后1條履行 exists:true 查詢,沒有顯示指定hint,其返回的winningPlan.stage=FETCH(使用索引掃描);
總結:
在某1個被創建了稀疏索引字段上履行exists:false查詢時,需要顯示指定hint,其索引才會起作用;而履行 exists:true查詢時,則不需要。
在字段上創建普通索引 ,如果文檔不含該字段這其索引值會被設為null,而稀疏索引會跳過該文檔;這就是說使用該索引掃描集合時稀疏索引會比普通索引少。
語法: db.collection.createIndex({“字段名”: 1或⑴,…”字段名n”: 1或⑴},{“partialFilterExpression”:{partialFilterExpression }})
partialFilterExpression表達式以下
equality expressions (i.e. field: value or using the $eq operator)
$exists: true expression,
$gt, $gte, $lt, $lte expressions,
$type expressions,
$and operator at the top-level only
示例:
db.restaurants.createIndex(
{ cuisine: 1, name: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)
(備注:地理空間索引,全文索引、TTL索引、哈希索引本人工作中很少使用到,這里就沒有描寫了)
db.collections.dropIndexes()
db.collections.dropIndex({"被創建索引的字段名":1})
如果希望修改1條索引,您需要刪除然后重建它
如果您需要重建集合中的索引,您可使用 db.collection.reIndex() ,1條操作就能夠重建這個集合上的所有索引。它會刪除所有索引,包括 _id 索引 ,然后重建所有索引。
(備注:其他索引在本人工作中很少使用過,這里就不描寫了)
索引策略
索引創建教程