MongoDB系列四(索引).
一、索引簡介
? ? 再來老生常談一番,什么是索引呢?數據庫索引與書籍的索引類似。有了索引就不需要翻整本書,數據庫可以直接在索引中查找,在索引中找到條目以后,就可以直接跳轉到目標文檔的位置,這能使查找速度提高幾個數量級。
? ??然而,使用索引是有代價的:對于添加的每一個索引,每次寫操作(插入、更新、刪除)都將耗費更多的時間。這是因為,當數據發生變動時,MongoDB不僅要更新文檔,還要更新集合上的所有索引。因此,MongoDB限制每個集合上最多只能有64個索引。通常,在一個特定的集合上,不應該擁有兩個以上的索引。于是,挑選合適的字段建立索引非常重要。
- 索引基數
基數(cardinality)就是集合中某個字段擁有不同值的數量。比如 gender 字段,基數一般就男女 2個而已;而像 mobile 這樣的字段,基數就會特別大。
通常來講,一個字段的基數越高,這個字段上的索引就越有用。這是因為索引能夠迅速將搜索范圍縮小到一個比較小的結果集。對于低基數的字段,索引通常無法排除掉大量可能的匹配。假設我們在"gender"上有一個索引,需要查找名為Susan的女性用戶。通過這個索引,只能將搜索空間縮小到大約50%。
tips:在關系型數據庫中類似 gender 這樣的字段可以使用位圖索引。
- 索引原理淺析
我們以一個索引?{"age" : 1, "username" : 1} 來看看索引在MongoDB 中是如何存儲的,大致是這個樣子:
每一個索引條目都包含一個"age"字段 和 "username"字段,并且指向文檔在磁盤中的存儲位置。注意,這里的 age 嚴格的按照升序排序,并且相同的 age 對應的 username 也嚴格的按照升序排序。
來看個例子 :db.users.find({"age" : 21}).sort({"username" : -1})
這個索引對于這個查詢來說是非常高效的,因為它可以馬上定位到 age = 21 的位置,并且age = 21 中的 username 已經是排序好的。
tips:排序方向并不重要:MongoDB可以在任意方向上對索引進行遍歷。
tips:查詢中的字段順序無關緊要,MongoDB 會自動找出可以使用索引的字段,而無視查詢的字段順序。
- $操作符如何使用索引
有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。
$where:無法使用索引。
$nin:無法使用索引。
$exists:無法使用索引。因為在索引中,不存在的字段和null字段的存儲方式是一樣的,查詢必須遍歷每一個文檔檢查這個值是否真的為null還是根本不存在。
$ne:可以使用索引,但并不是很高效。因為必須遍歷整個索引條目才能找到結果的文檔。
$not:能夠使用索引,但通常不知道如何使用索引,從而退化成全表掃描。
$or:能夠使用索引,但是$or 查詢會將 or 的條件拆分成多個獨立的查詢,然后再將結果合并在一起。這是很低效的,不建議用。建議用 $in 取代 $or 。
設計多鍵索引的時候要記得,要把基數大的字段放在索引的前面,因為這樣能更快縮小查詢的范圍。
二、索引類型
- 復合(組合)索引
復合索引就是一個建立在多個字段上的索引。
如果查詢中有多個排序方向或者查詢條件中有多個鍵,復合索引就非常有效。
db.userInfo.ensureIndex({"age":1,"age":1})?
進行多鍵排序時,索引的方向尤為重要。盡量做到多鍵排序的方向和復合索引的方向是一致的,因為這能很大的避免在內存中進行排序的運算。
tips:相互反轉(在每個方向上都乘以-1)的索引是等價的:{"age" : 1, "user name" : -1}適用的查詢與{"age" : -1, "username" : 1}是完全一樣的。
復合索引具有雙重功能,而且對不同的查詢可以表現為不同的索引。如果有一個{"age" :1, "username" : 1}索引,"age"字段會被自動排序,就好像有一個{"age" : 1}索引一樣。因此,這個復合索引可以當作{"age" : 1}索引一樣使用。
- 唯一索引
唯一索引可以確保集合的每一個文檔的指定鍵都有唯一值。我們熟悉的 "_id" 索引就是一個唯一索引(但它不能被刪除,而其他唯一索引是可以刪除的)。
db.users.ensureIndex({"username" : 1}, {"unique" : true})
定義了唯一索引后,這個鍵就不允許插入重復的值了,否則會拋異常。
tips:A 字段不存在 和 A 字段為 null 是互斥的!
在已有的集合上創建唯一索引可能會報錯,因為集合中可能已經有重復的值了。在極少數情況下,可能希望直接刪除重復的值。創建索引時使用"dropDups"選項,如果遇到重復的值,第一個會被保留,之后的重復文檔都會被刪除。
db.users.ensureIndex({"username" : 1}, {"unique" : true, "dropDups" : true})
- 稀疏索引
在有些情況下,你可能希望唯一索引只對包含相應鍵的文檔生效。如果有一個可能存在也可能不存在的字段,但是當它存在時,它必須是唯一的,這時就可以將unique和sparse選項組合在一起使用,創建唯一稀疏索引。注意:MongoDB中的稀疏索引(sparse index)與關系型數據庫中的稀疏索引是完全不同的概念。基本上來說,MongoDB中的稀疏索引只是不需要將每個文檔都作為索引條目。并且,稀疏索引并不一定是唯一的。
db.ensureIndex({"email" : 1}, {"unique" : true, "sparse" : true})
當某個查詢使用了稀疏索引,就不會返回不包含這個字段的文檔。因為稀疏索引并沒有把每個文檔都作為索引條目。
- 覆蓋索引
如果你的查詢只需要查找索引中包含的字段,那就根本沒必要獲取實際的文檔。當一個索引包含用戶請求的所有字段,可以認為這個索引覆蓋了本次查詢。所以,盡可能使用投射篩選返回的字段,比如 {"_id":0,"age":1} 等,來實現覆蓋索引。
三、索引管理
- 新建索引
普通索引
db.userInfo.ensureIndex({"name":1},{"name","MyIndex"})
"1" 表示按照name進行升序,"-1" 表示按照name進行降序。
默認的索引以 key1_1_key2_-1 這樣的方式命名,可以手動指定索引的名字,如上。
對象索引
可以對整個對象建立索引,或者對對象的某個元素使用索引。
db.users.ensureIndex({"loc" : 1})
只有在進行與對象字段順序完全匹配的子文檔查詢時(比如db.users.find({"loc" :{"ip" : "123.456.789.000", "city" : "Shelbyville", "state" :"NY"}}})),查詢優化器才會使用"loc"上的索引。
db.users.ensureIndex({"loc.city" : 1})
有涉及到對象city的查詢都會使用這個索引。
數組索引
?對數組建立索引,實際上是對數組的每個元素建立一個索引條目。比如一個文檔中的數組字段有20個元素,那么該文檔就擁有了20個索引條目!所以對數組字段的索引建立要慎重。
- 刪除索引
db.userInfo.dropIndexes("name_1")
刪除指定索引
db.userInfo.dropIndexes()
刪除除了_id 以外的所有索引
- 操作索引
獲取當前索引列表:db.userInfo.getIndexes()
hint 暴力選擇某種索引:db.userInfo.find({name:'zhangsan',birthday:'1989-3-2'}).hint({"name":1,"birthday":1})
強制使用全表掃描:db.userInfo.find({"birthday" : {"$lt" :"1989-3-2"}}).hint({"$natural" : 1})
索引分析函數explain:MongoDB 3.0前 和 MongoDB 3.0后存在很大的差異,這里只簡單說明下,如果想詳細了解的話,可以關注該作者的文章:
MongoDB 3.0 前:db.driverLocation.find({"areaCode":"350203"}).explain()
cursor:表掃描方式 (basicCursor:順序查找)
nscanned:瀏覽了多少文檔
n:最終返回了幾個文檔
millis:總共耗時了多少毫秒
scanAndOrder:是否必須在內存中對數據進行排序
MongoDB 3.0 后:db.driverLocation.find({"areaCode":"350203"}).explain("executionStats")
executionTimeMillis:該query的整體查詢時間
nReturned:查詢返回的條目
totalKeysExamined:索引掃描條目
totalDocsExamined:文檔掃描條目?
轉載于:https://www.cnblogs.com/jmcui/p/8757299.html
總結
以上是生活随笔為你收集整理的MongoDB系列四(索引).的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Idea中Terminal中git基本操
- 下一篇: 2018 Multi-Universit