上一篇文章: MongoDB指南---十二、使用explain()和hint()、什麼時候不該該使用索引
下一篇文章: MongoDB指南---1四、特殊的索引和集合:固定集合、TTL索引、全文本索引
建立索引時能夠指定一些選項,使用不一樣選項創建的索引會有不一樣的行爲。接下來的小節會介紹常見的索引變種,更高級的索引類型和特殊選項會在下一章介紹。數據庫
惟一索引能夠確保集合的每個文檔的指定鍵都有惟一值。例如,若是想保證同不文檔的"username"鍵擁有不一樣的值,建立一個惟一索引就行了:segmentfault
> db.users.ensureIndex({"username" : 1}, {"unique" : true})
若是試圖向上面的集合中插入以下文檔:服務器
> db.users.insert({username: "bob"}) > db.users.insert({username: "bob"}) E11000 duplicate key error index: test.users.$username_1 dup key: { : "bob" }
若是檢查這個集合,會發現只有第一個"bob"被保存進來了。發現有重複的鍵時拋出異常會影響效率,因此可使用惟一索引來應對偶爾可能會出現的鍵重複問題,而不是在運行時對重複的鍵進行過濾。
有一個惟一索引可能你已經比較熟悉了,就是"_id"索引,這個索引會在建立集合時自動建立。這就是一個正常的惟一索引(但它不能被刪除,而其餘惟一索引是能夠刪除的)。框架
若是一個文檔沒有對應的鍵,索引會將其做爲null存儲。因此,若是對某個鍵創建了惟一索引,但插入了多個缺乏該索引鍵的文檔,因爲集合已經存在一個該索引鍵的值爲null的文檔而致使插入失敗。5.4.2節會詳細介紹相關內容。
有些狀況下,一個值可能沒法被索引。索引儲桶(index bucket)的大小是有限制的,若是某個索引條目超出了它的限制,那麼這個條目就不會包含在索引裏。這樣會形成一些困惑,由於使用這個索引進行查詢時會有一個文檔憑空消失不見了。全部的字段都必須小於1024字節,才能包含到索引裏。若是一個文檔的字段因爲太大不能包含在索引裏,MongoDB不會返回任何錯誤或者警告。也就是說,超出8 KB大小的鍵不會受到惟一索引的約束:能夠插入多個一樣的8 KB長的字符串。函數
也能夠建立複合的惟一索引。建立複合惟一索引時,單個鍵的值能夠相同,但全部鍵的組合值必須是惟一的。
例如,若是有一個{"username" : 1, "age" : 1}上的惟一索引,下面的插入是合法的:性能
db.users.insert({"username" : "bob"})
db.users.insert({"username" : "bob", "age" : 23})
db.users.insert({"username" : "fred", "age" : 23})
然而,若是試圖再次插入這三個文檔中的任意一個,都會致使鍵重複異常。
GirdFS是MongoDB中存儲大文件的標準方式(詳見6.5節),其中就用到了複合惟一索引。存儲文件內容的集合有一個{"files_id" : 1, "n" : 1}上的複合惟一索引,所以文檔的某一部分看起來可能會是下面這個樣子:
{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 1}
{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 2}
{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 3}
{"files_id" : ObjectId("4b23c3ca7525f35f94b60a2d"), "n" : 4}
注意,全部"files_id"的值都相同,可是"n"的值不一樣。
在已有的集合上建立惟一索引時可能會失敗,由於集合中可能已經存在重複值了:spa
> db.users.ensureIndex({"age" : 1}, {"unique" : true}) E11000 duplicate key error index: test.users.$age_1 dup key: { : 12 }
一般須要先對已有的數據進行處理(可使用聚合框架),找出重複的數據,想辦法處理。
在極少數狀況下,可能但願直接刪除重複的值。建立索引時使用"dropDups"選項,若是遇到重複的值,第一個會被保留,以後的重複文檔都會被刪除。code
> db.people.ensureIndex({"username" : 1}, {"unique" : true, "dropDups" : true})
"dropDups"會強制性創建惟一索引,可是這個方式太粗暴了:你沒法控制哪些文檔被保留哪些文檔被刪除(若是有文檔被刪除的話,MongoDB也不會給出提示說哪些文檔被刪除了)。對於比較重要的數據,千萬不要使用"dropDups"。索引
前面的小節已經講過,惟一索引會把null看作值,因此沒法將多個缺乏惟一索引中的鍵的文檔插入到集合中。然而,在有些狀況下,你可能但願惟一索引只對包含相應鍵的文檔生效。若是有一個可能存在也可能不存在的字段,可是當它存在時,它必須是惟一的,這時就能夠將unique和sparse選項組合在一塊兒使用。資源
MongoDB中的稀疏索引(sparse index)與關係型數據庫中的稀疏索引是徹底不一樣的概念。基本上來講,MongoDB中的稀疏索引只是不須要將每一個文檔都做爲索引條目。
使用sparse選項就能夠建立稀疏索引。例如,若是有一個可選的email地址字段,可是,若是提供了這個字段,那麼它的值必須是惟一的:
> db.ensureIndex({"email" : 1}, {"unique" : true, "sparse" : true})
稀疏索引沒必要是惟一的。只要去掉unique選項,就能夠建立一個非惟一的稀疏索引。
根據是否使用稀疏索引,同一個查詢的返回結果可能會不一樣。假若有這樣一個集合,其中的大部分文檔都有一個"x"字段,可是有些沒有:
> db.foo.find() { "_id" : 0 } { "_id" : 1, "x" : 1 } { "_id" : 2, "x" : 2 } { "_id" : 3, "x" : 3 }
當在"x"上執行查詢時,它會返回相匹配的文檔:
> db.foo.find({"x" : {"$ne" : 2}}) { "_id" : 0 } { "_id" : 1, "x" : 1 } { "_id" : 3, "x" : 3 }
若是在"x"上建立一個稀疏索引,"_id"爲0的文檔就不會包含在索引中。若是再次在"x"上查詢,MongoDB就會使用這個稀疏索引,{"_id" : 0}的這個文檔就不會被返回了:
> db.foo.find({"x" : {"$ne" : 2}}) { "_id" : 1, "x" : 1 } { "_id" : 3, "x" : 3 }
若是須要獲得那些不包含"x"字段的文檔,可使用hint()強制進行全表掃描。
如前面的小節所述,可使用ensuerIndex函數建立新的索引。對於一個集合,每一個索引只須要建立一次。若是重複建立相同的索引,是沒有任何做用的。
全部的數據庫索引信息都存儲在system.indexes集合中。這是一個保留集合,不能在其中插入或者刪除文檔。只能經過ensureIndex或者dropIndexes對其進行操做。
建立一個索引以後,就能夠在system.indexes中看到它的元信息。能夠執行db.collectionName.getIndexes()來查看給定集合上的全部索引信息:
> db.foo.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.foo", "name" : "_id_" }, { "v" : 1, "key" : { "y" : 1 }, "ns" : "test.foo", "name" : "y_1" }, { "v" : 1, "key" : { "x" : 1, "y" : 1 }, "ns" : "test.foo", "name" : "x_1_y_1" } ]
這裏面最重要的字段是"key"和"name"。這裏的鍵能夠用在hint、max、min以及其餘全部須要指定索引的地方。在這裏,索引的順序很重要:{"x" : 1, "y" : 1}上的索引與{"y" : 1, "x" : 1}上的索引不一樣。對於不少的索引操做(好比dropIndex),這裏的索引名稱均可以被看成標識符使用。可是這裏不會指明索引是不是多鍵索引。
"v"字段只在內部使用,用於標識索引版本。若是你的索引不包含"v" : 1這樣的字段,說明你的索引是以一種效率比較低的舊方式存儲的。將MongoDB升級到至少2.0版本,刪除並重建這些索引,就能夠把索引的存儲方式升級到新的格式了。
集合中的每個索引都有一個名稱,用於惟一標識這個索引,也能夠用於服務器端來刪除或者操做索引。索引名稱的默認形式是key name1_dir1_keyname2_dir2_..._keynameN_dirN,其中keynameX是索引的鍵,dirX是索引的方向(1或者-1)。若是索引中包含兩個以上的鍵,這種命名方式就顯得比較笨重了,好在能夠在ensureIndex中指定索引的名稱:
> db.foo.ensureIndex({"a" : 1, "b" : 1, "c" : 1, ..., "z" : 1}, ... {"name" : "alphabet"})
索引名稱的長度是有限制的,因此新建複雜索引時可能須要自定義索引名稱。調用getLastError就能夠知道索引是否成功建立,或者失敗的緣由。
隨着應用不斷增加變化,你會發現數據或者查詢已經發生了改變,原來的索引也不那麼好用了。這時可使用dropIndex命令刪除再也不須要的索引:
> db.people.dropIndex("x_1_y_1") { "nIndexesWas" : 3, "ok" : 1 }
用索引描述信息裏"name"字段的值來指定須要刪除的索引。
新建索引是一件既費時又浪費資源的事情。默認狀況下,MongoDB會盡量快地建立索引,阻塞全部對數據庫的讀請求和寫請求,一直到索引建立完成。若是但願數據庫在建立索引的同時仍然可以處理讀寫請求,能夠在建立索引時指定background選項。這樣在建立索引時,若是有新的數據庫請求須要處理,建立索引的過程就會暫停一下,可是仍然會對應用程序性能有比較大的影響。後臺建立索引比前臺建立索引慢得多。
在已有的文檔上建立索引會比新建立索引再插入文檔快一點。
上一篇文章: MongoDB指南---十二、使用explain()和hint()、什麼時候不該該使用索引
下一篇文章: MongoDB指南---1四、特殊的索引和集合:固定集合、TTL索引、全文本索引