MongoDB索引原理和具体使用
1. MongoDB 索引是用來干嘛?
索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取數據時必須掃描集合中的每個文件并選取那些符合查詢條件的記錄。
這種掃描全集合的查詢效率是非常低的,特別在處理大量的數據時,查詢可以要花費幾十秒甚至幾分鐘,這對網站的性能是非常致命的。
索引是特殊的數據結構,索引存儲在一個易于遍歷讀取的數據集合中,索引是對數據庫表中一列或多列的值進行排序的一種結構
簡單舉例分析下:
假設這里有一個 commits 集合,我們想要查詢其中的數據!
db.commits.find({"eId":"5913cd0ee727e70007a109f2"}).explain("executionStats")
從執行計劃來看,我們從18284條數據中查詢得到了80條查詢結果集,而我們文檔中的記錄數就是18284條記錄,說明這條查詢語句是全表搜索,顯然這是非常浪費效率的。
2. 創建單索引
為了提高查詢效率,我們可以使用創建索引的方式,mongodb使用createIndex方法來創建索引語法:
db.collection_name.createIndex(keys[,options])這時我們給commits集合的eId這個字段創建索引
db.commits.createIndex({eId:1})索引創建成功之后,這時再查詢commits集合
db.commits.find({"eId":"5913cd0ee727e70007a109f2"}).explain("executionStats")我們查詢得到的數據是80條,而我們一共只在80條數據里查詢,從執行記劃來看,查詢的記錄數,查詢的時間明顯都縮小。
從上圖,如果我們想給sinfo下面的name加索引,可以這樣寫
db.commits.createIndex({"sinfo.name":1})我們不在要sinfo上面加索引,如果這樣做了,我們必須查詢整個子方檔。
createIndex()接收可選參數,可選參數列表例如以下:
| background | Boolean | 建索引過程會堵塞其他數據庫操作,background可指定以后臺方式創建索引,即添加 “background” 可選參數。 “background” 默認值為false。 |
| unique | Boolean | 建立的索引是否唯一。指定為true創建唯一索引。默認值為false. |
| name | string | 索引的名稱。假設未指定,MongoDB的通過連接索引的字段名和排序順序生成一個索引名稱。 |
| dropDups | Boolean | 在建立唯一索引時是否刪除反復記錄,指定 true 創建唯一索引。默認值為 false. |
| sparse | Boolean | 對文檔中不存在的字段數據不啟用索引。這個參數須要特別注意。假設設置為true的話,在索引字段中不會查詢出不包括相應字段的文檔.。默認值為 false. |
| expireAfterSeconds | integer | 指定一個以秒為單位的數值,完畢 TTL設定,設定集合的生存時間。 |
| version | index version | 索引的版本號號。默認的索引版本號取決于mongod創建索引時執行的版本號。 |
比如說:創建唯一索引,方法就是指定unique鍵為true,如:
db.users.createIndex({name:1},{“unique”:true})3. 復合索引
當我們的查詢條件不只有一個時,就需要建立復合索引。復合索引是兩個或更多字段的索引,并且它可以支持基于這些字段的查詢。當我們查詢時有多個過濾條件時,為了提高查詢效率,可以在這多個條件上添加索引,語法
db.collection_name.createIndex(key1:1,key2:1)當我們查詢條件為兩個時,如:
db.commits.find({"eId":"5913cd0ee727e70007a109f2","sId":"5913c700434a8b00077256fe"}).explain("executionStats")
因前面已經給eId創建了索引,所以本次查詢范圍從18248條記錄,降到了80條記錄,而sId沒有創建索引,所有我們從80條記錄里得到了符合eId,sId這兩個條件的38條記錄,那么我們現在給sId也創建索引
當查詢的兩個條件sId,eId都創建了索引后,再執行
db.commits.find({"eId":"5913cd0ee727e70007a109f2","sId":"5913c700434a8b00077256fe"}).explain("executionStats")4. 多鍵索引
如果文檔中含有array類型字段,可以直接對其名稱建立索引,這樣MongoDB就會為內嵌數組中的每個元素建立一個獨立的索引
注意:多鍵索引不等于在多列字段上創建索引(復合索引),多鍵索引與單鍵索引創建形式相同,差別在于字段的類型.
語法:
db.COLLECTION_NAME.createIndex({key:< 1 or -1 >})如:
有一個paper集合,數據表結構如下,其中structures字段是數組類型。
例如:這時我們給papers集合的structures這個字段創建索引
因為這個structures字段是數組,所有這個索引稱之為多鍵索引。
5.復合多鍵索引
對于一個復合多鍵索引,每個索引最多可以包含一個數組。在多于一個數組的情形下來創建復合多鍵索引不被支持。
假定存在如下集合
{ _id: 1, a: [ 1, 2 ], b: [ 1, 2 ], category: "AB - both arrays" }你可能會這樣創建索引db.COLLECTION_NAME.createIndex({a:1,b:1}),但是這樣是不允許的,因為a和b都是數組。
如果{a:1,b:1}的索引已經創建了,則a和b當中必定有一個是非array,此時插入一個a和b都是array的文檔就會失敗。
假定存在如下集合
{ _id: 1, a: [1, 2], b: 1, category: "A array" } { _id: 2, a: 1, b: [1, 2], category: "B array" }則可以基于每一個文檔創建一個基于{ a: 1, b: 1 }的復合多鍵索引,原因是每一個索引的索引字段只有一個數組
類似的,如下的內嵌文檔也可以建立索引。例如:
db.test.createIndex({“stock.size”:1,”stock.quantity”:1})6.部分索引
MongoDB部分索引只為那些在一個集合中,滿足指定的篩選條件的文檔創建索引。由于部分索引是一個集合文檔的一個子集,因此部分索引具有較低的存儲需求,并降低了索引創建和維護的性能成本。
部分索引通過指定過濾條件來創建,可以為MongoDB支持的所有索引類型使用部分索引。
簡單點說:部分索引就是帶有過濾條件的索引,即索引只存在與某些文檔之上
創建部分索引語法:
db.collection.createIndex(keys, options)options可以使用partialFilterExpression,即部分過濾表達式,其類型為文檔類型
過濾表達式通常包括:$exists, $gt, $gte, $lt, $lte,$type,$and
過濾表達式使用示例:
db.persons.createIndex({name:1},{partialFilterExpression:{age: {$gt:25}}})此句的意思是:基于age列創建大于25歲的部分索引。
創建的部分索引過濾條件是age大于25,
當查詢的條件是country等于china,age大于25。 條件滿足,從執行計劃里可以看出此次查詢采用索引掃描。
當查詢的條件是country等于china,age大于等于25。 條件不滿足,從執行計劃里可以看出此次查詢采用的是全表掃描方式。
創建部分唯一索引的一些限制:
部分索引只為集合中那些滿足指定的篩選條件的文檔創建索引。 如果你指定的partialfilterexpression和唯一約束、那么唯一性約束只適用于滿足篩選條件的文檔。 具有唯一約束的部分索引不會阻止不符合唯一約束且不符合過濾條件的文檔的插入。
示例文檔
db.users.insertMany([ { "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }, { "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }, { "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }])為集合添加索引
db.users.createIndex({ username: 1 }, { partialFilterExpression: { age: { $gte: 21 } } })在集合users上插入用戶名相同的文檔,收到了重復鍵的錯誤提示
db.users.insert( { username: "david", age: 27 } ) WriteResult({"nInserted" : 0,"writeError" : {"code" : 11000,"errmsg" : "E11000 duplicate key error collection: test.users index: username_1 dup key: { : \"david\" }"} })下面插入年齡小于部分索引值或者age鍵為空的同用戶名文檔,可以成功插入。
也就是說對于不在部分索引限制之類的其他鍵值重復是允許的。
7.文本索引
MongoDB提供文本索引以支持對字符串內容的文本搜索查詢。text索引可以包括其值為字符串或字符串元素數組的任何字段。
文本索引,顧名思義就是用于搜索文本的,可以用于搜索所有的value,也可以搜索指定的field對應的value。只要field對應value是string,或者對應的value是array且array中的元素是string,那么文本索引都可以索引該field
注意:一個集合最多只能有一個文本索引。
要創建text索引,請使用該 db.collection.createIndex()方法。要索引包含字符串或字符串元素數組的字段,請包含該字段并"text"在索引文檔中指定字符串文字。
如以下示例所示:
db.collection.createIndex({keys:”text”})也可以創建多個字段text,例如
db.collection.createIndex({subject:”text”,comments:”text”})舉例,有兩條記錄
{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst}{_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}給statement字段創建文本索引:
db.collection.createIndex({statement:”text”})查詢
db.collection.find({$text:{$search:”MongoDB best”}})查詢出來的結果集是:
{_id:5908df789dfd1fd5884fd84f7df4,statement:MongoDB is the worst}{_id:5908dfgfh587hgf15f4hf54hf418,statement:MongoDB is the best}這是因為文本查詢時,每個單詞之間的分隔是”或者”,所以上面的查詢語句的意思是:查詢包含MongoDB或者best的記錄數。
在多個字段上創建文本索引時,還可以使用通配符說明符($**)。使用通配符文本索引,MongoDB會為包含集合中每個文檔的字符串數據的每個字段編制索引。
例如:
db.collection.createIndex({“$**”,”text”})此索引允許在具有字符串內容的所有字段上進行文本搜索。如果不清楚要包含在文本索引中的哪些字段或用于臨時查詢
通配符文本索引可以是復合索引的一部分。例如,以下內容在字段nane和通配符說明符上創建復合索引,例如:
db.collection.createIndex({name:1,“$**”,”text”})排序操作無法從text索引獲取排序順序,即使是復合文本索引也是如此 ; 即排序操作不能使用文本索引中的排序。
8.唯一索引
只要指定了某個field是唯一的,那么在同一個集合中就不允許存在相同的field值,MongoDB默認創建的唯一索引就是_id。
唯一索引一般是這樣創建的:
單索引創建唯一索引,如:
db.persons.createIndex({name:1},{unique:true})復合鍵創建唯一索引,如:
db.persons.createIndex({name:1,email:1},{unique:true})9.查看索引:
MongoDB提供的查看索引信息的方法:
- getIndexes()方法可以用來查看集合的所有索引,
- getIndexKeys()方法查看索引鍵。
- totalIndexSize()查看集合索引的總大小,
- getIndexSpecs()方法查看集合各索引的詳細信息db
MongoDB默認會為插入的文檔生成_id字段(如果應用本身沒有指定該字段),_id是文檔唯一的標識,為了保證能根據文檔id快遞查詢文檔,MongoDB默認會為集合創建_id字段的索引。
例1: getIndexes()的用法
> db.exams.getIndexes() [{"v" : 2, //索引版本"key" : { //索引的字段及排序方向 "_id" : 1 //根據_id字段升序索引},"name" : "_id_", //索引的名稱"ns" : "Steam.exams" //集合名} ]例2:getIndexKeys()的用法
> db.exams.getIndexKeys() [ { "_id" : 1 } ]例3:totalIndexSize()的用法
> db.exams.totalIndexSize() 6553610.刪除索引:
不再需要的索引,我們可以將其刪除,mongodb提供兩種刪除索引的方法:
- dropIndex()方法用于刪除指定的索引
- dropIndexes()方法用于刪除全部的索引
例1:dropIndex()的用法
> db.users.dropIndex()11.注意:
11.1 MongoDB中索引是大小寫敏感的。
當更新對象是,只有在索引上的這些key發生變化時才會更新。著極大地提高了性能。當對象增長了或者必須移動時,所有的索引必須更新,這回很慢 。
索引信息會保存在system.indexes 集合中,運行 db.system.indexes.find() 能夠看到這些示例數據。
索引的字段的大小有最大限制,目前接近800 bytes. 可在大于這個值的字段上建立索引是可以的,但是該字段不會被索引,這種限制在以后的版本中可能被移除。
11.2 索引的性能
索引使得可以通過關鍵字段獲取數據,能夠使得快速查詢和更新數據。
但是,必須注意的是,索引也會在插入和刪除的時候增加一些系統的負擔。往集合中插入數據的時候,索引的字段必須加入到B-Tree中去。
因此,索引適合建立在讀遠多于寫的數據集上,對于寫入頻繁的集合,在某些情況下,索引反而有副作用。不過大多數集合都是讀頻繁的集合,所以集合在大多數情況下是有用的。
11.3 使用sort()而不需要索引
如果數據集合比較小(通常小于4M),使用sort()而不需要建立索引就能夠返回數據。在這種情況下,做好聯合使用limit()和sort()。
總結
以上是生活随笔為你收集整理的MongoDB索引原理和具体使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python操作redis用法详解
- 下一篇: 第44讲:scrapy中间键Middle