上一篇講到了MongoDB的基本操做增刪查改。對於查詢來講。必須依照咱們的查詢要求去集合中,並將查找到的結果返回。在這個過程當中事實上是對整個集合中每個文檔進行了掃描,假設知足咱們的要求就加入到結果集中最後返回。對於小集合來講。這個過程沒什麼,但是集合中數據很是大的時候,進行表掃描是一個很是恐怖的事情,因而有了索引一說,索引是用來加速查詢的。至關於書籍的文件夾。有了文件夾可以很是精準的定位要查找內容的位置,從而下降無謂的查找。css
1.索引的類型html
建立索引可以是在單個字段上,也可以是在多個字段上,這個依據本身的實際狀況來選擇,建立索引時字段的順序也是有講究的。數據庫
建立索引是經過ensureIndex()方法。需要給該方法傳遞一個文檔形式的數據,當中指定索引的字段和順序,1表明升序。-1表明降序。數組
1).默認索引dom
還記得"_id"嗎,這個字段的數據是不能反覆的。它就是MongoDB的默認索引。而且不能被刪除。工具
2).單列索引post
在單個字段上建立的索引就是單列索引,在查詢的過程當中可以對該加速對該鍵的查詢,然而對其它鍵的查詢是沒有幫助的。spa
單列索引的順序是不會影響對該鍵的隨即查詢。建立單列索引:.net
> db.people.ensureIndex({"name" : 1})
3).組合索引code
還可以在多個鍵上建立組合索引,此時鍵的位置和索引的順序都會影響查詢的效率。看如下建立組合索引:
> db.people.ensureIndex({"name" : 1, "age" : 1}) > db.people.ensureIndex({"age" : 1, "name" : 1})
第一種狀況會對name排序組織,當name同樣時在對age排序,因此對{"name" : 1}和{「name」 : 1, "age" : 1}的查詢更高效,而另一種狀況則對age排序。當age同樣再對name排序。因此對{"age" : 1}和{"age" : 1, "name" : 1}的查詢更高效。當組合索引包括很是多字段的時候。會對前幾個鍵的查詢有幫助。
4).內嵌文檔索引
還可以對內嵌文檔建立索引,和普通鍵建立索引同樣幾乎相同。也可以對內嵌文檔建立組合索引:
> db.people.ensureIndex({"friends.name" : 1}) > db.people.ensureIndex({"friends.name" : 1, "friends.age" : 1})
在來看看其它幾種形式的索引:
惟一索引 > db.people.ensureIndex({"name" : 1}, {"unique" : true}) > db.people.ensureIndex({"name" : 1}, {"unique" : true, "dropDups" : true}) 鬆散索引 > db.people.ensureIndex({"name" : 1}, {"sparse" : true}) 多值索引 > db.people.find() {"name" : ["mary", "rose"]} > db.people.ensureIndex({"name" : 1})
惟一索引unique可以保證該鍵相應的值在集合中是惟一的,假設建立惟一索引的時候,該字段原來就存在了反覆的數據,那麼就會建立失敗,可以加上dropDups字段來消除反覆數據。它會保留髮現的第一個文檔。其它有反覆數據的文檔都將被刪除。
集合中有的文檔不存在某些字段。或者某些字段的值爲null,那麼咱們在該字段上建立索引的時候不但願讓這些空值的文檔參與。那麼就定義爲鬆散索引sparse。比方在name上建立索引時,發現有的人在數據庫中僅僅有學號。沒有名字。那麼咱們不但願把它們也包括進來。此時就定義爲鬆散索引。
一個鍵相應的值是一個數組,在該鍵上建立索引時是一個多值索引。會爲數組中每個值生成一個索引元素。至關於分裂成了幾個獨立的索引項,但是它們仍是相應同一個文檔數據。
2.索引的管理
索引當然是爲查詢而生,而且可以爲每個鍵都建立索引。但是索引是需要存儲空間的,因此索引不是越多越好,而且建立索引後。每次的插入,更新和刪除文檔都會產生額外的開銷,因爲數據庫中不但要運行這些操做,而且還要在集合索引中標記這些操做。因此要依據實際狀況來建立索引,索引沒用以後將其刪除。
建立索引是ensureIndex()方法,建立完畢後可以經過getIndexes()來查看集合中建立的索引狀況:
> db.people.ensureIndex({"name" : 1, "age" : 1}) > db.people.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.people", "name" : "_id_" }, { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "ns" : "test.people", "name" : "name_1_age_1" } ]
可以看到people集合建立了兩個索引,一個是"_id",這個是默認索引。另一個是name和age的組合索引,名字爲keyname1_dir_keyname2_dir_...,keyname表明索引的鍵,dir表明方向。1表明升序,-1表明降序。
固然咱們也可以本身定義索引的名稱:
> db.people.ensureIndex({"name" : 1, "age" : 1}, {"name" : "myIndex"}) > db.people.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.people", "name" : "_id_" }, { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "ns" : "test.people", "name" : "myIndex" } ]
刪除索引是經過dropIndex():
方式一: > db.people.dropIndex({"name" : 1, "age" : 1}) { "nIndexesWas" : 2, "ok" : 1 } 方式二: > db.runCommand({"dropIndexes" : "people", "index" : "myIndex"}) { "nIndexesWas" : 2, "ok" : 1 }
索引的元信息存儲在每個數據庫的system.indexes集合中。不能對其進行插入和刪除文檔的操做。僅僅能經過ensureIndex和dropIndex進行。
> db.system.indexes.find() { "v" : 1, "key" : { "_id" : 1 }, "ns" : "test.people", "name" : "_id_" } { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "ns" : "test.people", "name" : "myIndex" }
清空集合中全部的文檔是不會將索引刪除的,原來建立的索引依舊存在,但是直接刪除集合的話,該集合的索引也是會被刪除的。
3.索引的效率
假設咱們定義了很是多的索引,那麼MongoDB會依據咱們的查詢選項又一次排序,並智能的選擇一個最優的來使用。比方咱們建立了{"name" : 1, "age" : 1}和{"age" : 1, "class" : 1}兩個索引,但是咱們的查詢項爲find({"age" : 10, "name" : "mary"}),那麼MongoDB會本身主動又一次排序爲find({"name" : "mary", "age" : 10}),而且利用索引{"name" : 1, "age" : 1}來查詢。
MongoDB提供了explain工具來幫助咱們得到查詢方面的很是多實用信息,僅僅要對遊標調用這種方法就可以獲得查詢的細節。如下給math集合中加入10W個文檔。再來看看使用索引先後的效率對照:
> var arr = []; > for(var i = 0; i < 100000; i++){ ... var doc = {}; ... var value = Math.floor(Math.random() * 1000); ... doc["number"] = value; ... arr.push(doc); ... } 100000 > db.math.insert(arr) > db.math.count() 100000 > db.math.find().limit(10) { "_id" : ObjectId("53a7f7c6e4fd24348ce61fe5"), "number" : 462 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fe6"), "number" : 123 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fe7"), "number" : 90 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fe8"), "number" : 46 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fe9"), "number" : 244 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fea"), "number" : 972 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61feb"), "number" : 925 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fec"), "number" : 110 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fed"), "number" : 739 } { "_id" : ObjectId("53a7f7c6e4fd24348ce61fee"), "number" : 945 }
經過for循環給arr數組中加入10W條數據。而後再批量插入這些數據到math集合中。查看前10條數據,因爲是隨即生成的值。因此number字段的值會有反覆值。咱們就來查詢462這個值:
建立索引前: > db.math.find({"number" : 462}).explain() { "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 94, "nscannedObjects" : 100000, "nscanned" : 100000, "nscannedObjectsAllPlans" : 100000, "nscannedAllPlans" : 100000, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 35, "indexBounds" : { }, "server" : "server0.169:9352" } 建立索引後: > db.math.ensureIndex({"number" : 1}) > db.math.find({"number" : 462}).explain() { "cursor" : "BtreeCursor number_1", "isMultiKey" : false, "n" : 94, "nscannedObjects" : 94, "nscanned" : 94, "nscannedObjectsAllPlans" : 94, "nscannedAllPlans" : 94, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "number" : [ [ 462, 462 ] ] }, "server" : "server0.169:9352" }
這裏來看一下實用的信息,"cursor"指用的哪一個索引。"nscanned"表明查找了多少個文檔。"n"指返回文檔的數量。"millis"表示查詢所花時間。單位是毫秒。可以看出建立索引前沒有使用索引。在全部的文檔中查詢的,花費了35毫秒。而建立索引後,使用了number_1索引查詢。索引存儲在B樹結構中,僅僅在94個文檔中查詢,差點兒不花時間。
假設有很是多索引的話,MongoDB會本身主動選一個來查詢,你也可以經過hint來強制使用某個索引,這裏強制使用{"age" : 1, "name" : 1}這個索引:
> db.people.find({"age" : {"$gt" : 10}, "name" : "mary"}).hint({"age" : 1, "name" : 1})