數據庫中的索引就是用來提升查詢操做的性能,可是會影響插入、更新和刪除的效率,由於數據庫不只要執行這些操做,還要負責索引的更新。shell
經過創建索引,影響一部分插入、更新和刪除的效率,可是能大大挺高查詢的效率,這個仍是很值得的。數據庫
爲了開始後面的操做,首先經過MongoDB shell插入一些測試數據。dom
1 for(var i=0;i<10;i++){ 2 var randAge = parseInt(5*Math.random()) + 20; 3 var gender = (randAge%2)?"Male":"Female"; 4 db.school.students.insert({"name":"Will"+i, "gender": gender, "age": randAge}); 5 } 6 7 8 /* 個人數據,如下測試都是基於這個測試,因爲數據是隨機生成,因此測試每次都會不一樣 9 { "name" : "Will0", "gender" : "Female", "age" : 22 }, 10 { "name" : "Will1", "gender" : "Female", "age" : 20 }, 11 { "name" : "Will2", "gender" : "Male", "age" : 24 }, 12 { "name" : "Will3", "gender" : "Male", "age" : 23 }, 13 { "name" : "Will4", "gender" : "Male", "age" : 21 }, 14 { "name" : "Will5", "gender" : "Male", "age" : 20 }, 15 { "name" : "Will6", "gender" : "Female", "age" : 20 }, 16 { "name" : "Will7", "gender" : "Female", "age" : 24 }, 17 { "name" : "Will8", "gender" : "Male", "age" : 21 }, 18 { "name" : "Will9", "gender" : "Female", "age" : 24 }, 19 */
建立索引:在MongoDB shell中,能夠經過ensureIndex()來建立因此,第一個參數是指定要建立因此的鍵。性能
經過unique參數能夠建立惟一索引。測試
1 > db.school.students.ensureIndex({"name": 1}, {"unique": true})
2 >
查看索引:優化
1 > db.school.students.getIndexes() 2 [ 3 { 4 "v" : 1, 5 "key" : { 6 "_id" : 1 7 }, 8 "ns" : "test.school.students", 9 "name" : "_id_" 10 }, 11 { 12 "v" : 1, 13 "key" : { 14 "name" : 1 15 }, 16 "unique" : true, 17 "ns" : "test.school.students", 18 "name" : "name_1" 19 } 20 ] 21 >
刪除索引:spa
1 > db.school.students.dropIndex("name_1") 2 { "nIndexesWas" : 2, "ok" : 1 } 3 >
索引名稱:默認狀況下,索引的名稱是"鍵_值_鍵_值…"的形式,當鍵的數量不少的時候,索引的名字就會很長。code
因此,在建立索引的時候,能夠經過"name"參數自定義索引的名字。server
1 > db.school.students.ensureIndex({"name": 1}, {"name": "myIndex"}) 2 >
經過explain()能夠獲得不少跟find相關的信息,對索引的分析頗有幫助。blog
當有多個可使用的索引時,MongoDB會自動選擇最優索引,可是咱們能夠經過hint()操做選擇咱們想要使用的索引。
下面來看看沒有索引時explain()的輸出:
1 > db.school.students.find({"name": "Will5"}).explain() 2 { 3 "cursor" : "BasicCursor", 4 "isMultiKey" : false, 5 "n" : 1, 6 "nscannedObjects" : 6, 7 "nscanned" : 6, 8 "nscannedObjectsAllPlans" : 6, 9 "nscannedAllPlans" : 6, 10 "scanAndOrder" : false, 11 "indexOnly" : false, 12 "nYields" : 0, 13 "nChunkSkips" : 0, 14 "millis" : 0, 15 "indexBounds" : { 16 17 }, 18 "server" : "××××:27017" 19 } 20 >
分析:下面選擇了幾個咱們比較關心的字段
添加索引,再次檢查explain()的輸出:
1 > db.school.students.ensureIndex({"name": 1}, {"unique": true}) 2 > db.school.students.find({"name": "Will5"}).explain() 3 { 4 "cursor" : "BtreeCursor name_1", 5 "isMultiKey" : false, 6 "n" : 1, 7 "nscannedObjects" : 1, 8 "nscanned" : 1, 9 "nscannedObjectsAllPlans" : 1, 10 "nscannedAllPlans" : 1, 11 "scanAndOrder" : false, 12 "indexOnly" : false, 13 "nYields" : 0, 14 "nChunkSkips" : 0, 15 "millis" : 0, 16 "indexBounds" : { 17 "name" : [ 18 [ 19 "Will5", 20 "Will5" 21 ] 22 ] 23 }, 24 "server" : "××××:27017" 25 } 26 >
單鍵索引仍是比較簡單的,當使用組合索引的時候,就要多考慮一些了。本身也不肯定可否總結的很好,若是錯誤,但願你們指出、討論。
索引創建可能有多種方式,咱們的目標就是減小"nscanned"(固然也有特例,請參照"索引和排序")。
下面分析基於前面生成的數據來分析一下組合索引,假設咱們要查詢年齡大於等於23的女學生。
1 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).hint("age_1").explain() 2 { 3 "cursor" : "BtreeCursor age_1", 4 "isMultiKey" : false, 5 "n" : 2, 6 "nscannedObjects" : 4, 7 "nscanned" : 4, 8 "nscannedObjectsAllPlans" : 4, 9 "nscannedAllPlans" : 4, 10 "scanAndOrder" : false, 11 "indexOnly" : false, 12 "nYields" : 0, 13 "nChunkSkips" : 0, 14 "millis" : 0, 15 "indexBounds" : { 16 "age" : [ 17 [ 18 23, 19 1.7976931348623157e+308 20 ] 21 ] 22 }, 23 "server" : "××××:27017" 24 } 25 >
索引的分析:
Index |
Documents |
Result |
age:20 |
{ "name" : "Will1", "gender" : "Female", "age" : 20 } |
"n" : 2 |
age:20 |
{ "name" : "Will5", "gender" : "Male", "age" : 20 } |
"nscannedObjects" : 4 |
age:20 |
{ "name" : "Will6", "gender" : "Female", "age" : 20 } |
"nscanned" : 4 |
age:21 |
{ "name" : "Will4", "gender" : "Male", "age" : 21 } |
|
age:21 |
{ "name" : "Will8", "gender" : "Male", "age" : 21 } |
|
age:22 |
{ "name" : "Will0", "gender" : "Female", "age" : 22 } |
|
age:23 |
{ "name" : "Will3", "gender" : "Male", "age" : 23 } |
|
age:24 |
{ "name" : "Will2", "gender" : "Male", "age" : 24 } |
|
age:24 |
{ "name" : "Will7", "gender" : "Female", "age" : 24 } |
|
age:24 |
{ "name" : "Will9", "gender" : "Female", "age" : 24 } |
1 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).hint("age_1_gender_1").explain() 2 { 3 "cursor" : "BtreeCursor age_1_gender_1", 4 "isMultiKey" : false, 5 "n" : 2, 6 "nscannedObjects" : 2, 7 "nscanned" : 4, 8 "nscannedObjectsAllPlans" : 2, 9 "nscannedAllPlans" : 4, 10 "scanAndOrder" : false, 11 "indexOnly" : false, 12 "nYields" : 0, 13 "nChunkSkips" : 0, 14 "millis" : 0, 15 "indexBounds" : { 16 "age" : [ 17 [ 18 23, 19 1.7976931348623157e+308 20 ] 21 ], 22 "gender" : [ 23 [ 24 "Female", 25 "Female" 26 ] 27 ] 28 }, 29 "server" : "××××:27017" 30 } 31 >
索引的分析:
Index |
Documents |
Result |
age:20, gender:Female |
{ "name" : "Will1", "gender" : "Female", "age" : 20 } |
"n" : 2 |
age:20, gender:Female |
{ "name" : "Will6", "gender" : "Female", "age" : 20 } |
"nscannedObjects" : 2 |
age:20, gender:Male |
{ "name" : "Will5", "gender" : "Male", "age" : 20 } |
"nscanned" : 4 |
age:21, gender:Male |
{ "name" : "Will4", "gender" : "Male", "age" : 21 } |
|
age:21, gender:Male |
{ "name" : "Will8", "gender" : "Male", "age" : 21 } |
|
age:22, gender:Female |
{ "name" : "Will0", "gender" : "Female", "age" : 22} |
|
age:23, gender:Male |
{ "name" : "Will3", "gender" : "Male", "age" : 23 } |
|
age:24, gender:Female |
{ "name" : "Will7", "gender" : "Female", "age" : 24 } |
|
age:24, gender:Female |
{ "name" : "Will9", "gender" : "Female", "age" : 24 } |
|
age:24, gender:Male |
{ "name" : "Will2", "gender" : "Male", "age" : 24 } |
1 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).hint("gender_1_age_1").explain() 2 { 3 "cursor" : "BtreeCursor gender_1_age_1", 4 "isMultiKey" : false, 5 "n" : 2, 6 "nscannedObjects" : 2, 7 "nscanned" : 2, 8 "nscannedObjectsAllPlans" : 2, 9 "nscannedAllPlans" : 2, 10 "scanAndOrder" : false, 11 "indexOnly" : false, 12 "nYields" : 0, 13 "nChunkSkips" : 0, 14 "millis" : 0, 15 "indexBounds" : { 16 "gender" : [ 17 [ 18 "Female", 19 "Female" 20 ] 21 ], 22 "age" : [ 23 [ 24 23, 25 1.7976931348623157e+308 26 ] 27 ] 28 }, 29 "server" : "××××:27017" 30 } 31 >
索引的分析:
Index |
Documents |
Result |
gender:Female, age:20 |
{ "name" : "Will1", "gender" : "Female", "age" : 20 } |
"n" : 2 |
gender:Female, age:20 |
{ "name" : "Will6", "gender" : "Female", "age" : 20 } |
"nscannedObjects" : 2 |
gender:Female, age:22 |
{ "name" : "Will0", "gender" : "Female", "age" : 22 } |
"nscanned" : 2 |
gender:Female, age:24 |
{ "name" : "Will7", "gender" : "Female", "age" : 24 } |
|
gender:Female, age:24 |
{ "name" : "Will9", "gender" : "Female", "age" : 24 } |
|
gender:Male, age:20 |
{ "name" : "Will5", "gender" : "Male", "age" : 20 } |
|
gender:Male, age:21 |
{ "name" : "Will4", "gender" : "Male", "age" : 21 } |
|
gender:Male, age:21 |
{ "name" : "Will8", "gender" : "Male", "age" : 21 } |
|
gender:Male, age:23 |
{ "name" : "Will3", "gender" : "Male", "age" : 23 } |
|
gender:Male, age:24 |
{ "name" : "Will2", "gender" : "Male", "age" : 24 } |
經過上面的例子能夠看出,在使用組合索引的時候仍是要考慮不少東西的,因此能夠結合explain()來進行分析。
因爲咱們前面建立了三個索引,下面咱們直接使用默認查詢。
1 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).explain() 2 { 3 "cursor" : "BtreeCursor gender_1_age_1", 4 "isMultiKey" : false, 5 "n" : 2, 6 "nscannedObjects" : 2, 7 "nscanned" : 2, 8 "nscannedObjectsAllPlans" : 2, 9 "nscannedAllPlans" : 2, 10 "scanAndOrder" : false, 11 "indexOnly" : false, 12 "nYields" : 0, 13 "nChunkSkips" : 0, 14 "millis" : 0, 15 "indexBounds" : { 16 "gender" : [ 17 [ 18 "Female", 19 "Female" 20 ] 21 ], 22 "age" : [ 23 [ 24 23, 25 1.7976931348623157e+308 26 ] 27 ] 28 }, 29 "server" : "××××:27017" 30 } 31 >
存在多條索引的狀況下,MongoDB首選nscanned值最低的索引。
基於上面的例子,咱們加上對"name"的排序操做。這時,咱們能夠看到"scanAndOrder"變成了"true"。
1 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).sort({"name":1}).explain() 2 { 3 "cursor" : "BtreeCursor gender_1_age_1", 4 "isMultiKey" : false, 5 "n" : 2, 6 "nscannedObjects" : 2, 7 "nscanned" : 2, 8 "nscannedObjectsAllPlans" : 7, 9 "nscannedAllPlans" : 9, 10 "scanAndOrder" : true, 11 "indexOnly" : false, 12 "nYields" : 0, 13 "nChunkSkips" : 0, 14 "millis" : 0, 15 "indexBounds" : { 16 "gender" : [ 17 [ 18 "Female", 19 "Female" 20 ] 21 ], 22 "age" : [ 23 [ 24 23, 25 1.7976931348623157e+308 26 ] 27 ] 28 }, 29 "server" : "××××:27017" 30 }
在這個例子中,"nscanned"是最小的,因此這個方案是查詢效率最高的。可是,咱們要注意一下"scanAndOrder",根據MongoDB文檔的解釋,查詢結果的排序不能利用現有的索引,MongoDB會把find找到的結果放入內存從新排序。這樣的話,若是數據量很大,會對性能產生很大的影響。
最好的辦法是利用索引來進行排序。
在這種狀況下,就要加入一個"name"的索引,同時在find操做時使用hint來指定索引方式,由於默認狀況MongoDB會選擇"nscanned"最小的方式。
1 > db.school.students.ensureIndex({"gender":1,"name":1}) 2 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).sort({"name":1}).hint("gender_1_name_1").explain() 3 { 4 "cursor" : "BtreeCursor gender_1_name_1", 5 "isMultiKey" : false, 6 "n" : 2, 7 "nscannedObjects" : 5, 8 "nscanned" : 5, 9 "nscannedObjectsAllPlans" : 5, 10 "nscannedAllPlans" : 5, 11 "scanAndOrder" : false, 12 "indexOnly" : false, 13 "nYields" : 0, 14 "nChunkSkips" : 0, 15 "millis" : 0, 16 "indexBounds" : { 17 "gender" : [ 18 [ 19 "Female", 20 "Female" 21 ] 22 ], 23 "name" : [ 24 [ 25 { 26 "$minElement" : 1 27 }, 28 { 29 "$maxElement" : 1 30 } 31 ] 32 ] 33 }, 34 "server" : "xxxx:27017" 35 } 36 >
經過這種方式,就能夠利用索引的排序來避免"scanAndOrder"爲true的狀況。可是再看看上面的方式,彷佛能夠進一步優化,雖然不能減小"nscanned",可是能夠減小"nscannedObjects"。
1 > db.school.students.ensureIndex({"gender":1,"name":1,"age":1}) 2 > db.school.students.find({"age":{"$gte":23}, "gender":"Female"}).sort({"name":1}).hint("gender_1_name_1_age_1").explain() 3 { 4 "cursor" : "BtreeCursor gender_1_name_1_age_1", 5 "isMultiKey" : false, 6 "n" : 2, 7 "nscannedObjects" : 2, 8 "nscanned" : 5, 9 "nscannedObjectsAllPlans" : 2, 10 "nscannedAllPlans" : 5, 11 "scanAndOrder" : false, 12 "indexOnly" : false, 13 "nYields" : 0, 14 "nChunkSkips" : 0, 15 "millis" : 0, 16 "indexBounds" : { 17 "gender" : [ 18 [ 19 "Female", 20 "Female" 21 ] 22 ], 23 "name" : [ 24 [ 25 { 26 "$minElement" : 1 27 }, 28 { 29 "$maxElement" : 1 30 } 31 ] 32 ], 33 "age" : [ 34 [ 35 23, 36 1.7976931348623157e+308 37 ] 38 ] 39 }, 40 "server" : "xxxx:27017" 41 } 42 >
MongoDB中,索引還有不少東西,本文只是經過一些例子來介紹了索引的使用,以及組合索引的簡單分析
Ps: 本文中全部例子中的命令均可以參考如下連接
http://files.cnblogs.com/wilber2013/index.js