標籤:
索引可以用來最佳化查詢,而且在某些特定類型的查詢中,索引是必不可少的。為集合選擇合適的索引是提高效能的關鍵。
先來mock資料
for (i = 0; i < 1000000; i++) { db.users.insert({ "i": i, "username": "user" + i, "age": Math.floor(Math.random() * 120), "created": new Date() });}
資料庫中會建立一百萬條資料,稍微有點慢,需要等會。
我們可以使用explain()函數查看MongoDB在執行查詢的過程中所做的事情。執行如下命令,尋找使用者名稱為user1000的使用者。
db.users.find({username:"user1000"}).explain()
得到結果如下:
{ "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1000000, "nscanned" : 1000000, "nscannedObjectsAllPlans" : 1000000, "nscannedAllPlans" : 1000000, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 7813, "nChunkSkips" : 0, "millis" : 411, "server" : "user:27017", "filterSet" : false}
之後會詳細介紹各個欄位的意思,現在我們只需要知道,"n"表示查詢結果的數量,"nscanned"表示MongoDB在完成這個查詢的過程中掃描的檔案總數,"millis"表示這個查詢耗費的毫秒數。可以看到,為了尋找user1000,MongoDB遍曆了整個集合,消耗了411毫秒。
為了最佳化查詢,我們可以在尋找到一個結果的時候,就結束查詢,返回結果。命令如下:
db.users.find({username:"user1000"}).limit(1).explain()
結果如下:
{ "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1001, "nscanned" : 1001, "nscannedObjectsAllPlans" : 1001, "nscannedAllPlans" : 1001, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 7, "nChunkSkips" : 0, "millis" : 1, "server" : "user:27017", "filterSet" : false}
可以看到掃描文檔數和消耗時間都變少了很多,但是如果我們要尋找user999999,MongoDB還是要遍曆集合才能找到。而且隨著使用者數量的增多,查詢會越來越慢。
對於這種情況,建立索引是一個非常好的解決方案:索引可以根據給定的欄位組織資料,讓MongoDB能夠非常快速的找到目的文件。使用如下命令,在username欄位上建立一個索引。
db.users.ensureIndex({"username":1})
然後再來執行一下之前執行過的語句
db.users.find({username:"user1000"}).explain()
其結果如下:
{ "cursor" : "BtreeCursor username_1", "isMultiKey" : false, "n" : 1, "nscannedObjects" : 1, "nscanned" : 1, "nscannedObjectsAllPlans" : 1, "nscannedAllPlans" : 1, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "username" : [ [ "user1000", "user1000" ] ] }, "server" : "user:27017", "filterSet" : false}
然後你會發現查詢變快了很多,幾乎是瞬間完成,這就是使用索引的效果。但是索引也是有代價的,對於添加的每一個索引,每次寫操作(插入、更新、刪除)都將耗費更多的時間。這是因為當資料發生變動時,MongoDB不僅要更新文檔,還要更新集合上的所有索引。因此,MongoDB限制每個集合上最多隻能有64個索引。通常,在一個特定的集合上,不應該擁有兩個以上的索引。
當一個索引建立在多個欄位上時,我們稱它為複合索引,建立的語句如下:
db.users.ensureIndex({"age":1, "username":1})
如果查詢中有多個排序方向或者查詢條件中有多個鍵,複合索引就會非常有用。
MongoDB對這個索引的使用方法取決於查詢的類型。下面是三種主要的方式。
第一種:
db.users.find({"age":21}).sort({"username":-1})
這是一個點查詢,用於尋找單個值(儘管包含這個值的文檔是多個)。由於索引中的第二個欄位,查詢結果已經是有序的了。這種類型的查詢是非常高效的。
第二種:
db.users.find({"age":{"$gte":21,"$lte":30}})
這是一個多值查詢,尋找到多個值相匹配的文檔,MongoDB會使用索引中的第一個鍵“age”得到匹配文檔。如果使用“username”做查詢,該索引不起作用。
第三種:
db.users.find({"age":{"$gte":21,"$lte":30}}).sort({"username":1})
這也是一個多值查詢,與上一個類似,只是這次需要對查詢結果進行排序。MongoDB需要在記憶體中對結果進行排序,不如上一個高效。
刪除索引的命令如下:
db.users.dropIndex(‘age_1_username_1‘)
刪除users集合中名字為‘age_1_username_1‘的索引。
所有資料庫的索引資訊都儲存在system.indexes集合中,這是一個保留集合,不能在其中插入或者刪除文檔,只能通過ensureIndex和dropIndex對其進行操作。
使用如下命令可以擷取users集合上的索引資訊:
db.users.getIndexes()
結果如下:
[ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.users" }, { "v" : 1, "key" : { "username" : 1 }, "name" : "username_1", "ns" : "test.users" }, { "v" : 1, "key" : { "age" : 1, "username" : 1 }, "name" : "age_1_username_1", "ns" : "test.users" }]
MongoDB的學習(3)--索引