復(fù)合索引<接上>
創(chuàng)新互聯(lián)主要從事成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)納雍,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):13518219792
> 刪除之前的collection,重新建立,如下所示:
> db.person.drop()
true
> for(var i=0;i<2000000;i++){
... db.person.insert({"name":"meteor"+i%1000,"age":20+i%10});
... }
WriteResult({ "nInserted" : 1 })
>
> db.person.ensureIndex({"age":1}) 創(chuàng)建單一索引
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
> db.person.ensureIndex({"name":1,"age":1}) 創(chuàng)建復(fù)合索引 {"name":1,"age":1}
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 }
> db.person.ensureIndex({"age":1,"name":1}) 創(chuàng)建復(fù)合索引{"age":1,"name":1}
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 3, "numIndexesAfter" : 4, "ok" : 1 }
> 查找時(shí)指定多個(gè)條件,使用hint強(qiáng)制指定使用單一索引速度比較慢,如下所示:
> db.person.find({"age":{"$gte":20,"$lte":30},"name":"meteor1"}).hint({"age":1}).explain("executionStats")
{ "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.person", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "name" : { "$eq" : "meteor1" } }, { "age" : { "$lte" : 30 } }, { "age" : { "$gte" : 20 } } ] }, "winningPlan" : { "stage" : "FETCH", "filter" : { "name" : { "$eq" : "meteor1" } }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "age" : 1 }, "indexName" : "age_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "age" : [ "[20.0, 30.0]" ] } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 2000, "executionTimeMillis" : 2621, "totalKeysExamined" : 2000000, "totalDocsExamined" : 2000000, "executionStages" : { "stage" : "FETCH", "filter" : { "name" : { "$eq" : "meteor1" } }, "nReturned" : 2000, "executionTimeMillisEstimate" : 2050, "works" : 2000001, "advanced" : 2000, "needTime" : 1998000, "needYield" : 0, "saveState" : 15625, "restoreState" : 15625, "isEOF" : 1, "invalidates" : 0, "docsExamined" : 2000000, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 2000000, "executionTimeMillisEstimate" : 640, "works" : 2000001, "advanced" : 2000000, "needTime" : 0, "needYield" : 0, "saveState" : 15625, "restoreState" : 15625, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "age" : 1 }, "indexName" : "age_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "age" : [ "[20.0, 30.0]" ] }, "keysExamined" : 2000000, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0 } } }, "serverInfo" : { "host" : "meteor.yeecall.com", "port" : 27027, "version" : "3.2.8", "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0" }, "ok" : 1 }
> 查找數(shù)據(jù)時(shí)指定多個(gè)條件,使用hint強(qiáng)制指定使用{"age":1,"name":1}復(fù)合索引,速度比較快,如下所示:
> db.person.find({"age":{"$gte":20,"$lte":30},"name":"meteor1"}).hint({"age":1,"name":1}).explain("executionStats")
{ "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.person", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "name" : { "$eq" : "meteor1" } }, { "age" : { "$lte" : 30 } }, { "age" : { "$gte" : 20 } } ] }, "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "age" : 1, "name" : 1 }, "indexName" : "age_1_name_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "age" : [ "[20.0, 30.0]" ], "name" : [ "[\"meteor1\", \"meteor1\"]" ] } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 2000, "executionTimeMillis" : 15, "totalKeysExamined" : 2010, "totalDocsExamined" : 2000, "executionStages" : { "stage" : "FETCH", "nReturned" : 2000, "executionTimeMillisEstimate" : 10, "works" : 2011, "advanced" : 2000, "needTime" : 10, "needYield" : 0, "saveState" : 15, "restoreState" : 15, "isEOF" : 1, "invalidates" : 0, "docsExamined" : 2000, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 2000, "executionTimeMillisEstimate" : 10, "works" : 2011, "advanced" : 2000, "needTime" : 10, "needYield" : 0, "saveState" : 15, "restoreState" : 15, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "age" : 1, "name" : 1 }, "indexName" : "age_1_name_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "age" : [ "[20.0, 30.0]" ], "name" : [ "[\"meteor1\", \"meteor1\"]" ] }, "keysExamined" : 2010, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0 } } }, "serverInfo" : { "host" : "meteor.yeecall.com", "port" : 27027, "version" : "3.2.8", "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0" }, "ok" : 1 }
=================================================================================
查詢結(jié)束再次排序(按name排序),并使用limit截取其中一部分,使用hint強(qiáng)制指定使用{"age":1,"name":1}索引時(shí)速度較慢,如下所示:
> db.person.find({"age":{"$gte":20,"$lte":30}}).sort({"name":1}).limit(100).hint({"age":1,"name":1}).explain("executionStats")
{ "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.person", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "age" : { "$lte" : 30 } }, { "age" : { "$gte" : 20 } } ] }, "winningPlan" : { "stage" : "SORT", "sortPattern" : { "name" : 1 }, "limitAmount" : 100, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "inputStage" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "age" : 1, "name" : 1 }, "indexName" : "age_1_name_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "age" : [ "[20.0, 30.0]" ], "name" : [ "[MinKey, MaxKey]" ] } } } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 100, "executionTimeMillis" : 6991, "totalKeysExamined" : 2000000, "totalDocsExamined" : 2000000, "executionStages" : { "stage" : "SORT", "nReturned" : 100, "executionTimeMillisEstimate" : 5980, "works" : 2000103, "advanced" : 100, "needTime" : 2000002, "needYield" : 0, "saveState" : 15625, "restoreState" : 15625, "isEOF" : 1, "invalidates" : 0, "sortPattern" : { "name" : 1 }, "memUsage" : 6100, "memLimit" : 33554432, "limitAmount" : 100, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "nReturned" : 0, "executionTimeMillisEstimate" : 5680, "works" : 2000002, "advanced" : 0, "needTime" : 1, "needYield" : 0, "saveState" : 15625, "restoreState" : 15625, "isEOF" : 1, "invalidates" : 0, "inputStage" : { "stage" : "FETCH", "nReturned" : 2000000, "executionTimeMillisEstimate" : 4870, "works" : 2000001, "advanced" : 2000000, "needTime" : 0, "needYield" : 0, "saveState" : 15625, "restoreState" : 15625, "isEOF" : 1, "invalidates" : 0, "docsExamined" : 2000000, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 2000000, "executionTimeMillisEstimate" : 2400, "works" : 2000001, "advanced" : 2000000, "needTime" : 0, "needYield" : 0, "saveState" : 15625, "restoreState" : 15625, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "age" : 1, "name" : 1 }, "indexName" : "age_1_name_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "age" : [ "[20.0, 30.0]" ], "name" : [ "[MinKey, MaxKey]" ] }, "keysExamined" : 2000000, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0 } } } } }, "serverInfo" : { "host" : "meteor.yeecall.com", "port" : 27027, "version" : "3.2.8", "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0" }, "ok" : 1 } >
查詢結(jié)束再次排序(按name排序),并使用limit截取其中一部分,使用hint強(qiáng)制指定使用{"name":1,"age":1}索引時(shí)速度較快,如下所示:
> db.person.find({"age":{"$gte":20,"$lte":30}}).sort({"name":1}).limit(100).hint({"name":1,"age":1}).explain("executionStats")
{ "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.person", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "age" : { "$lte" : 30 } }, { "age" : { "$gte" : 20 } } ] }, "winningPlan" : { "stage" : "LIMIT", "limitAmount" : 100, "inputStage" : { "stage" : "FETCH", "filter" : { "$and" : [ { "age" : { "$lte" : 30 } }, { "age" : { "$gte" : 20 } } ] }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "name" : 1, "age" : 1 }, "indexName" : "name_1_age_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "name" : [ "[MinKey, MaxKey]" ], "age" : [ "[MinKey, MaxKey]" ] } } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 100, "executionTimeMillis" : 5, "totalKeysExamined" : 100, "totalDocsExamined" : 100, "executionStages" : { "stage" : "LIMIT", "nReturned" : 100, "executionTimeMillisEstimate" : 0, "works" : 101, "advanced" : 100, "needTime" : 0, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "limitAmount" : 100, "inputStage" : { "stage" : "FETCH", "filter" : { "$and" : [ { "age" : { "$lte" : 30 } }, { "age" : { "$gte" : 20 } } ] }, "nReturned" : 100, "executionTimeMillisEstimate" : 0, "works" : 100, "advanced" : 100, "needTime" : 0, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 0, "invalidates" : 0, "docsExamined" : 100, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 100, "executionTimeMillisEstimate" : 0, "works" : 100, "advanced" : 100, "needTime" : 0, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 0, "invalidates" : 0, "keyPattern" : { "name" : 1, "age" : 1 }, "indexName" : "name_1_age_1", "isMultiKey" : false, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "name" : [ "[MinKey, MaxKey]" ], "age" : [ "[MinKey, MaxKey]" ] }, "keysExamined" : 100, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0 } } } }, "serverInfo" : { "host" : "meteor.yeecall.com", "port" : 27027, "version" : "3.2.8", "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0" }, "ok" : 1 }
> 如果按age排序,索引使用{"name":1,"age":1}速度非常慢;如果按age排序,索引使用{"age":1,"name":1}速度比較快
>分析:第一種索引,需要找到所有復(fù)合查詢條件的值(依據(jù)索引,鍵和文檔可以快速找到),但是找到后,需要對(duì)文檔在內(nèi)存中進(jìn)行排序,這個(gè)步驟消耗了非常多的時(shí)間。第二種索引,效果非常好,因?yàn)椴恍枰趦?nèi)存中對(duì)大量數(shù)據(jù)進(jìn)行排序。但是,MongoDB不得不掃描整個(gè)索引以便找到所有文檔。因此,如果對(duì)查詢結(jié)果的范圍做了限制,那么MongoDB在幾次匹配之后就可以不再掃描索引,在這種情況下,將排序鍵放在第一位是一個(gè)非常好的策略。
查看索引
> db.person.getIndexes()
[ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.person" }, { "v" : 1, "key" : { "age" : 1 }, "name" : "age_1", "ns" : "test.person" }, { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "name" : "name_1_age_1", "ns" : "test.person" }, { "v" : 1, "key" : { "age" : 1, "name" : 1 }, "name" : "age_1_name_1", "ns" : "test.person" } ]
> db.person.dropIndex("name_1_age_1") 刪除索引
{ "nIndexesWas" : 4, "ok" : 1 }
> db.person.dropIndex("age_1_name_1")
{ "nIndexesWas" : 3, "ok" : 1 }
> db.person.dropIndex("age_1")
{ "nIndexesWas" : 2, "ok" : 1 }
> db.runCommand({dropIndexes:"person",index:"*"}) 刪除索引的另一種方法
{ "nIndexesWas" : 1, "msg" : "non-_id indexes dropped for collection", "ok" : 1 }
> db.person.ensureIndex({"name":1,"age":1},{"unique":true}) 創(chuàng)建唯一索引 (本例沒(méi)有成功,因?yàn)榧现杏兄貜?fù)內(nèi)容)
{ "ok" : 0, "errmsg" : "E11000 duplicate key error collection: test.person index: name_1_age_1 dup key: { : \"meteor0\", : 20.0 }", "code" : 11000 }
MongoDB索引限制
額外開(kāi)銷
每個(gè)索引占據(jù)一定的存儲(chǔ)空間,在進(jìn)行插入,更新和刪除操作時(shí)也需要對(duì)索引進(jìn)行操作。所以,如果你很少對(duì)集合進(jìn)行讀取操作,建議不使用索引。
內(nèi)存(RAM)使用
由于索引是存儲(chǔ)在內(nèi)存(RAM)中,應(yīng)該確保該索引的大小不超過(guò)內(nèi)存的限制。(如上文中提示sort排序后,如果沒(méi)有l(wèi)imit字段系統(tǒng)會(huì)提示錯(cuò)誤,因?yàn)樗饕笮〕^(guò)了內(nèi)存的限制)
如果索引的大小大于內(nèi)存的限制,MongoDB會(huì)刪除一些索引,這將導(dǎo)致性能下降。
查詢限制
索引不能被以下的查詢使用:正則表達(dá)式及非操作符,如 $nin, $not,等;算術(shù)運(yùn)算符,如 $mod,等;$where子句
所以,檢測(cè)語(yǔ)句是否使用索引是一個(gè)好的習(xí)慣,可以用explain來(lái)查看。
索引鍵限制
從2.6版本開(kāi)始,如果現(xiàn)有的索引字段的值超過(guò)索引鍵的限制,MongoDB中不會(huì)創(chuàng)建索引。
插入文檔超過(guò)索引鍵限制
如果文檔的索引字段值超過(guò)了索引鍵的限制,MongoDB不會(huì)將任何文檔轉(zhuǎn)換成索引的集合。與mongorestore和mongoimport工具類似。
最大范圍
集合中索引不能超過(guò)64個(gè);索引名的長(zhǎng)度不能超過(guò)125個(gè)字符
一個(gè)復(fù)合索引最多可以有31個(gè)字段