一、MongoDB索引
1、索引介紹
索引在mongodb中被支持,如果沒有索引,mongodb必須掃描每一個文檔集合選擇匹配的查詢記錄。這樣掃描集合效率並不高,因爲它需要mongod進程使用大量的數據作遍歷操作。 索引是一種特殊的數據結構,它保存了小部分簡單的集合數據。索引存儲了一些特殊字段,並將其排序。
從根本上講,索引在mongodb中和其他數據庫系統是類似的。mongodb規定了索引的集合級別、支持索引任何字段或者子字段在mongodb文檔集合中。
2、索引優化查詢方案
要考慮數據之間的關係,做查詢優化。
創建索引支持常見的面向用戶的查詢,確保掃描讀取文件最小數量。
索引可以優化特定場景中的其它業務的性能。
3、排序返回數據
來看看一個索引的具體例子(其實就相當於我們查詢字段一樣的)
4、索引類型
MongoDB提供了一些不同的索引類型支持的數據和查詢的具體類型
Default _id (默認_id索引) :所有mongodb默認都有一個_id字段索引,如果我們不指定_id的值會自動生成一個ObjectId值。 該_id索引是唯一的,並且可以防止客戶端對_id字段值相同插入兩個。
# 查詢articles集合的索引 db.articles.getIndexes(); # 添加titlei字段索引,並且爲升序 db.articles.ensureIndex({title:1}); #重構索引(慎用) db.articles.reIndex();
注意:索引排序規則升序:1,降序-1
Single Field (單字段索引) :mongodb允許定義單個字段的索引,與default _id一樣,只是字段不同。
Compound Index (複合索引[多字段索引]) :mongodb中可以自定多個字段的索引。例如,如果一個複合指標包括{userid:1,score:-1 },索引排序第一的用戶名後,在每一個用戶標識符值,按得分++倒序++排序。
{ "_id": ObjectId(...), "item": "Banana", "category": ["food", "produce", "grocery"], "location": "4th Street Store", "stock": 4, "type": "cases", "arrival": Date(...) }
創建方法:
# 創建item、stock字段的複合索引,並且升序排序 db.products.ensureIndex( { "item": 1, "stock": 1 } ) 注意:Hashed 字段不能創建索引,如果創建將出現錯誤 Application Sort Order 使用案例:降序用戶名升序時間。 # 查詢結果集中排序 db.events.find().sort( { username: -1, date: 1 } ) # 查詢結果集中排序 db.user_scores.find().sort({score:-1,date:-1}).limit(1)
MultiKey Index (多鍵索引)
官方文檔中給出這樣一個案例:
{ userid:"marker", address:[ {zip:"618255"}, {zip:"618254"} ] } # 創建索引,並將zip升序排列 db.users.ensureIndex({"address.zip": 1}); # 假如我們做這樣的查詢 db.users.find({"addr":{"$in":[{zip:"618254"}]}})
注意:你可以創建 多鍵複合索引(multikey compound indexes)
Geospatial Index (地理空間索引)
db.places.ensureIndex( { loc : "2dsphere" } )
Text Indexes (文本索引) :文本索引是在2.4版本更新的,提供了文本搜索文檔中的集合功能,文本索引包含:字符串、字符數組。使用$text做查詢操作。 2.6版本 默認情況下使文本搜索功能。在MongoDB 2.4,你需要使文本搜索功能手動創建全文索引和執行文本搜索
# 創建文本索引 db.articles.ensureIndex({content:"text"});
複合索引可以包含文本索引 稱爲:複合文本索引(compound text indexes),但有限制 :
複合文本索引不能包含任何其他特殊索引類型,比如:多鍵索引(multi-key Indexes)
如果複合文本索引包含文本索引的鍵,執行$text查詢必須相同查詢條件。可能翻譯不對原文:
(If the compound text index includes keys preceding the text index key, to perform a $text search, the query
predicate must include equality match conditions on the preceding keys1)
Hashed Indexes (哈希碼索引) :哈希索引在2.4版本更新的,將實體的的哈希值作爲索引字段,
# 給user_scores的score字段創建一個哈希索引
db.user_scores.ensureIndex( { score: "hashed" } )
5、索引的屬性
除了衆多索引類型的支持以外,還可以使用各種屬性來調整性能。
TTL Indexes :它是一個特殊的索引,可以在某個時間自動的刪除文檔集合的索引。對於一些信息數據比如說日誌、事件對象、會話信息,只需要存放在數據庫一個特定期限。
使用限制:
不支持複合索引
必須是date時間類型字段
如果是date數組,按照最早時間過期。
注意:TTL index不保證過期時間立即刪除,,後臺任務沒60秒運行刪除, 依賴於mongod進程。
Unique Indexes
# 創建唯一索引 db.members.ensureIndex( { "user_id": 1 }, { unique: true } )
注意:如果字段爲null,那麼就以null值,但不能重複插入空值。如果collection中有兩個實體唯一索引字段爲空,則不能創建唯一索引
也就是說,我們還可以利用它作爲類似於關係型數據庫的唯一約束。
# 強制插入空值對象後報錯 > db.users.insert({content:"unique testing"}) WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicat e key error index: test.users.$dsadsadsa dup key: { : null }" } })
Sparse Indexes
db.addresses.ensureIndex( { "xmpp_id": 1 }, { sparse: true } )
background屬性 高效修改/創建索引 :在項目運行中,如果我們直接採用前面的方法創建索引或者修改索引,那麼數據庫會阻塞建立索引期間的所有請求。mongodb提供了background屬性做後臺處理。
db.addresses.ensureIndex( { "xmpp_id": 1 }, {background: true } )
我們知道如果阻塞所有請求,建立索引就會很快,但是使用系統的用戶就需要等待,影響了數據庫的操作,因此可以更具具體情況來選擇使用background屬性
6、索引名稱
# 自動生成索引名稱 db.products.ensureIndex( { item: 1, quantity: -1 } ) # 被命名爲: item_1_quantity_-1 # 自定義索引名稱 db.products.ensureIndex( { item: 1, quantity: -1 } , { name: "inventory" } )
7 、管理索引
# 添加/修改索引 db.users.ensureIndex({name:"text"}); # 刪除集合所有索引 db.users.dropIndexes(); # 刪除特定索引 (刪除id字段升序的索引) db.users.dropIndex({"id":1}) # 獲取集合索引 db.users.getIndexes(); # 重構索引 db.users.reIndex();
8、索引分類
Default _id (默認_id索引)
Single Field (單字段索引)
Compound Index (複合索引[多字段索引])
MultiKey Index (多鍵索引)
Geospatial Index (地理空間索引)
Text Indexes (文本索引)
Hashed Indexes (哈希碼索引)
二、應用舉例
創建單字段索引:
> db.testcoll.ensureIndex({Name: 1}) > db.testcoll.getIndexes() [ { "v" : 1, "name" : "_id_", "key" : { "_id" : 1 }, "ns" : "test.testcoll" }, { "v" : 1, "name" : "Name_1", "key" : { "Name" : 1 }, "ns" : "test.testcoll" } ]
創建哈希索引:
> db.testcoll.ensureIndex({Name: "hashed"}) > db.testcoll.getIndexes() [ { "v" : 1, "name" : "_id_", "key" : { "_id" : 1 }, "ns" : "test.testcoll" }, { "v" : 1, "name" : "Name_1", "key" : { "Name" : 1 }, "ns" : "test.testcoll" }, { "v" : 1, "name" : "Name_hashed", "key" : { "Name" : "hashed" }, "ns" : "test.testcoll" } ]
刪除哈希索引:
> db.testcoll.dropIndex("Name_hashed") { "nIndexesWas" : 3, "ok" : 1 } > db.testcoll.getIndexes() [ { "v" : 1, "name" : "_id_", "key" : { "_id" : 1 }, "ns" : "test.testcoll" }, { "v" : 1, "name" : "Name_1", "key" : { "Name" : 1 }, "ns" : "test.testcoll" } ]
刪除單字段索引:
> db.testcoll.dropIndex({Name: 1}) { "nIndexesWas" : 2, "ok" : 1 } > db.testcoll.getIndexes() [ { "v" : 1, "name" : "_id_", "key" : { "_id" : 1 }, "ns" : "test.testcoll" } ]
刪除所有索引:
> db.testcoll.dropIndexes()
查看索引是否用到:
> db.testcoll.find({Name: "User:88"}).explain() { "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 100, "nscanned" : 100, "nscannedObjectsAllPlans" : 100, "nscannedAllPlans" : 100, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 42, "indexBounds" : { }, "server" : "www.example.com:27017" } > db.testcoll.find().count() 100
通過創建索引可以發現在查詢時只需要一次就行,不用在全文檔掃描:
> db.testcoll.ensureIndex({Name: 1}),{unique: true} { "unique" : true } > db.testcoll.find({Name: "User:88"}).explain() { "cursor" : "BtreeCursor Name_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1, "nscanned" : 1, "nscannedObjectsAllPlans" : 1, "nscannedAllPlans" : 1, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "Name" : [ [ "User:88", "User:88" ] ] }, "server" : "www.example.com:27017" }
指定索引查詢:
> db.testcoll.find({Name: "User:88"}).hint({Name: 1}).explain()
總結:
前面只是簡單的介紹了MongoDB的索引及其的使用,在查詢時我們最好可以根據創建的索引來進行查詢,這樣做可以大大節省磁盤IO的開銷。若查詢時沒有相關的索引則極可能遍歷全部文檔,這樣大大降低了性能。在實際使用中創建和使用索引要做到:根據實際的需求創建最合適索引,查詢時也要使用最合適的索引。對於索引的使用前面所講的只是一些皮毛知識,若想深入學習建議可以查閱《數據庫索引設計與優化》。