1、簡介
它就像是一本書的目錄,如果沒有它,我們就需要對整個書籍進行查找來獲取需要的結果,即所說的全盤掃描;
而有了目錄(索引)之后就可以通過它幫我們定位到目標所在的位置,快速的獲取我們想要的結果。
2、演示
第一步,向用戶集合users中插入100W條數據
var insertUsers = function() { var start = new Date().getTime(); for (var i = 1; i <= 1000000; i++) { db.users.insert({ "userid": i, "username": "wjg" + i, "age": Math.floor(Math.random() * 100), //年齡為0~99的隨機整數 "createdate": new Date() }) } var end = new Date().getTime(); print("插入100W條數據共耗時" + (end - start) / 1000 + "秒");}
LZ的渣渣I3和4G內存總共耗時了484.623秒,約8分多鐘。任務管理器里邊可以很清楚的看到當時CPU、內存和磁盤使用率都普遍的增高。
第二步:查詢用戶名為“wjg465413”的文檔對象
db.users.find({username:"wjg465413"}).explain("allPlansExecution"){ "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.users", "indexFilterSet" : false, "parsedQuery" : { "username" : { "$eq" : "wjg465413" } }, "winningPlan" : { "stage" : "COLLSCAN", "filter" : { "username" : { "$eq" : "wjg465413" } }, "direction" : "forward" }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 1, "executionTimeMillis" : 865, "totalKeysExamined" : 0, "totalDocsExamined" : 1000000, "executionStages" : { "stage" : "COLLSCAN", "filter" : { "username" : { "$eq" : "wjg465413" } }, "nReturned" : 1, "executionTimeMillisEstimate" : 770, "works" : 1000002, "advanced" : 1, "needTime" : 1000000, "needFetch" : 0, "saveState" : 7813, "restoreState" : 7813, "isEOF" : 1, "invalidates" : 0, "direction" : "forward", "docsExamined" : 1000000 }, "allPlansExecution" : [ ] }, "serverInfo" : { "host" : "Jack", "port" : 27017, "version" : "3.0.3", "gitVersion" : "b40106b36eecd1b4407eb1ad1af6bc60593c6105" }, "ok" : 1}
說明:這里的explain方法相當于查詢計劃,它會返回給你查詢過程的詳細信息。它的參數有三種模式:“queryPlanner”(查詢計劃[默認])、“executionStats”(執行狀態)和“allPlansExecution”(所有執行計劃),這里我們只關注它返回給我們的以下幾個信息。
"executionTimeMillis" : 865 //執行的毫秒數 注:如果你是第一次執行,可能會花費更長的時間"totalDocsExamined" : 1000000 //共檢查的文檔數
第三步:在用戶名“username”字段上加上索引
db.users.createIndex({ "username" : 1 })
重新執行上次的查詢操作
db.users.find({username:"wjg465413"}).explain("allPlansExecution"){ "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.users", "indexFilterSet" : false, "parsedQuery" : { "username" : { "$eq" : "wjg465413" } }, "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "username" : 1 }, "indexName" : "username_1", "isMultiKey" : false, "direction" : "forward", "indexBounds" : { "username" : [ "[/"wjg465413/", /"wjg465413/"]" ] } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 1, "executionTimeMillis" : 53, "totalKeysExamined" : 1, "totalDocsExamined" : 1, "executionStages" : { "stage" : "FETCH", "nReturned" : 1, "executionTimeMillisEstimate" : 0, "works" : 2, "advanced" : 1, "needTime" : 0, "needFetch" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "docsExamined" : 1, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 1, "executionTimeMillisEstimate" : 0, "works" : 2, "advanced" : 1, "needTime" : 0, "needFetch" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "username" : 1 }, "indexName" : "username_1", "isMultiKey" : false, "direction" : "forward", "indexBounds" : { "username" : [ "[/"wjg465413/", /"wjg465413/"]" ] }, "keysExamined" : 1, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0, "matchTested" : 0 } }, "allPlansExecution" : [ ] }, "serverInfo" : { "host" : "Jack", "port" : 27017, "version" : "3.0.3", "gitVersion" : "b40106b36eecd1b4407eb1ad1af6bc60593c6105" }, "ok" : 1}
可以看到兩次的查詢計劃有很大的差別,我們還是著重看下那兩個屬性值。
"executionTimeMillis" : 53 //執行的毫秒數 "totalDocsExamined" : 1 //共檢查的文檔數
加過索引之后查詢這個文檔所耗費的時間僅僅為53毫秒,并且掃描一次直接定位,性能提升了16倍。可見合理使用索引的重要性!
注:“_id”字段是Mongo為我們默認添加的索引,而且是唯一索引,保證了數據的唯一性,不可以移除。另外,使用limit(1)限制查詢結果的數量也可以提高查詢速度
3、索引的類型
a)、單一索引:可以在數據集上任意一個字段上建立索引,包括普通的屬性鍵、內嵌文檔以及內嵌文檔中的屬性鍵。
db.users.createIndex({ "username" : 1 }) //普通屬性鍵的索引//假設class是一個內嵌的文檔db.users.createIndex({ "class" : 1 }) //內嵌文檔的索引 db.users.createIndex({ "class.classname" : 1 }) //內嵌文檔中的屬性鍵索引
索引方向:1表示升序,-1表示降序
b)、復合索引:以多個屬性鍵為基礎而建立得索引
db.users.createIndex({ "username" : 1, "age" : -1, "userid" : 1 }) //在“username”、“age”和“userid”上建立復合索引
索引前綴:通過建立上邊的復合索引之后,Mongo就相當于同時擁有了三個索引一樣,分別是{"username" : 1},{"username" : 1, "age" : -1}和{"username" : 1, "age" : -1, "userid" : 1},但是像{"age" : -1},{"userid" : 1}或者{"age" : -1, "userid" : 1}這三個索引并不會起作用。所以它會使用包含了前綴(首個)的索引的作為復合索引
c)、多鍵索引:為數組中的多個值建立索引以實現高效查詢。
注:Ⅰ、不允許在多個數組上建立復合索引
Ⅱ、不能指定片鍵作為多鍵索引
Ⅲ、哈希索引不能是多鍵
Ⅳ、多鍵索引不支持覆蓋查詢
d)、地理空間索引和查詢:Mongo提供了兩種曲面類型的索引:2dsphere索引和2d索引。查詢類型包括:包含(inclusion),交叉(intersection)和接近(proximity)
e)、文本索引:用來支持查詢包含了字符串或者字符串數組的文檔
db.users.createIndex({"username" : "text"})
注:文本索引不支持排序并且一個復合文本索引不能再包含其他任何索引了
f)、哈希索引:它可以在使用了哈希片鍵進行分片的數據集上進行索引,支持相等查詢,但是不支持范圍查詢
db.users.createIndex({"username" : "hashed"})
4、索引特性
a)、TTL(Time-To-Live)索引:是一種具有生命周期的索引,它允許為每一個文檔設置一個超時時間
db.users.createIndex({ "createdate" : 1 },{ "expireAfterSecs" : 60*60*24 })
說明:在“createdate”字段上建立一個TTL索引,當這個自段存在并且是日期類型,當服務器時間比“createdate”字段的時間晚60*60*24秒,即24小時時,文檔就會被刪除
b)、唯一索引:確保集合的每一個文檔的指定鍵都有唯一值
db.users.createIndex({"username" : 1}, {"unique" : true})
c)、稀疏索引:Mongo里邊的null會被看做值,如果有一個可能存在也可能不存在的字段,我們可以使用稀疏索引
db.users.createIndex({"age" : 1},{"sparse" : true})
4、索引操作
a)、查看所有索引
db.users.getIndexes()
b)、移除索引
db.users.dropIndex({"createdate1" : 1 })
c)、移除所有索引
db.users.dropIndexes()
d)、重建索引
db.users.reIndex()
說明:該操作會先刪除所有索引,包括“_id”,然后重新創建所有索引
新聞熱點
疑難解答