練習 MongoDB 操做 —— 索引篇(二)

本文圍繞索引、遊標兩部分進行探索,對MongoDB數據庫的索引部分有一個大概的瞭解;html

索引

索引一般可以極大的提升查詢的效率,若是沒有索引,MongoDB在讀取數據時必須掃描集合中的每一個文件並選取那些符合查詢條件的記錄。這種掃描全集合的查詢效率是很是低的,特別在處理大量的數據時,查詢能夠要花費幾十秒甚至幾分鐘,這對網站的性能是很是致命的。正則表達式

索引是特殊的數據結構,索引存儲在一個易於遍歷讀取的數據集合中,索引是對數據庫表中一列或多列的值進行排序的一種結構。索引,從建立的形式上能夠分爲普通索引複合索引數組索引子文檔索引mongodb

應該瞭解的索引限制,shell

  • 開銷:每一個索引佔據必定的存儲空間,在進行插入,更新和刪除操做時也須要對索引進行操做。因此,若是你不多對集合進行讀取操做,建議不使用索引;
  • 內存(RAM)使用:因爲索引是存儲在內存中,你應該確保該索引的大小不超過內存的限制。若是索引的大小大於內存的限制,MongoDB會刪除一些索引,這將致使性能降低。
  • 最大範圍:數據庫

    (1)集合中索引不能超過 64 個;
    (2)索引名的長度不能超過 125 個字符;
    (3)一個複合索引最多能夠有 31 個字段
  • 索引不能被如下的查詢使用;能夠調用explain()方法查看是否使用索引;數組

    (1)正則表達式及非操做符,如 `$nin`, `$not`, 等
    (2)算術運算符,如 `$mod` 等
    (3)`$where` 子句

操做

建立索引

爲集合grade_1_4根據性別和年齡建立複合索引:數據結構

db.getCollection('grade_1_4').ensureIndex({"sex": 1, "age": 1})

會獲得結果:dom

{
    "v" : 2,                  // 索引的版本號。默認的索引版本取決於mongod建立索引時運行的版本
    "key" : {
        "sex" : 1,
        "age" : 1
    },
    "name" : "sex_1_age_1",
    "ns" : "school.grade_1_4"
}

說明:基於sexage的查詢將會用到該複合索引,或者是基於sex的查詢也會用到該索引,可是隻是基於age的查詢將不會用到該複合索引。所以能夠說,若是想用到複合索引,必須在查詢條件中包含複合索引中的前N個索引列。然而若是查詢條件中的鍵值順序和複合索引中的建立順序不一致的話,MongoDB能夠智能的幫助咱們調整該順序,以便使複合索引能夠爲查詢所用性能

若是分別爲性別和年齡建立索引:學習

db.getCollection('grade_1_4').ensureIndex({"sex": 1})
db.getCollection('grade_1_4').ensureIndex({"age": 1})

會獲得這樣的結果:

{
    "v" : 2,
    "key" : {
        "sex" : 1
    },
    "name" : "sex_1",
    "ns" : "school.grade_1_4"
},
{
    "v" : 2,
    "key" : {
        "age" : 1
    },
    "name" : "age_1",
    "ns" : "school.grade_1_4"
}

查看索引

查看集合grade_1_4已經建立的索引規則:

db.getCollection('grade_1_4').getIndexes()

查看集合grade_1_4索引佔用內存空間的大小,單位是字節:

db.getCollection('grade_1_4').totalIndexSize()

刪除索引

刪除集合grade_1_4namesex_1的索引規則:

db.getCollection('grade_1_4').dropIndex({"sex": 1})

惟一索引

建立惟一索引與普通索引的區別在於,多一個可選參數 unique;舉個例子,根據姓名建立惟一索引:

db.getCollection('grade_1_4').ensureIndex({"name":1}, {"unique":true})

建立惟一索引可以保證每條記錄的name字段值是不重複的;

當一個文檔以惟一索引的方式保存到集合中去的時候,任何缺失的索引字段都會以null值代替,所以,不能在惟一索引上同時插入兩條缺省的記錄。

假設集合已經存在一些記錄,在些基礎上建立惟一索引;此時,對這些已經存在的記錄,若是索引項值是存在重複的,則建立索引時會報錯;若是必定要在這樣的鍵上建立惟一索引,那麼系統將保存第一條記錄,剩下的記錄會被刪除,結合dropDups參數使用(此參數只能在mongodb 3.0版本以前使用):

db.getCollection('grade_1_4').ensureIndex({"name":1}, {unique:true, dropDups: true})

稀疏索引

稀疏索引的建立和徹底索引的建立沒有什麼不一樣。使用稀疏索引進行查詢的時候,某些因爲缺失了字段的文檔記錄可能不會被返回,這是因爲稀疏索引子返回被索引了的字段。

示例:

> db.people.ensureIndex({title:1},{sparse:true}) //在title字段上創建稀疏索引
> db.people.save({name:"Jim"})
> db.people.save({name:"yang",title:"prince"})
> db.people.find();
{ "_id" : ObjectId("4e244dc5cac1e3490b9033d7"), "name" : "Jim" }
{ "_id" : ObjectId("4e244debcac1e3490b9033d8"), "name" : "yang", "title" : "prince" }

> db.people.find().sort({title:1})//自有包含有索引字段的記錄才被返回
{ "_id" : ObjectId("4e244debcac1e3490b9033d8"), "name" : "yang", "title" : "prince" }

> db.people.dropIndex({title:1})//刪除稀疏索引以後,全部的記錄均顯示
{ "nIndexesWas" : 2, "ok" : 1 }

> db.people.find().sort({title:1})
{ "_id" : ObjectId("4e244dc5cac1e3490b9033d7"), "name" : "Jim" }
{ "_id" : ObjectId("4e244debcac1e3490b9033d8"), "name" : "yang", "title" : "prince" }

性能示例

建立三十萬條數據

for (var i = 1; i <= 300000; i++) {
    db.getCollection('grade_1_5').insert({
       "name": "zhangsan" + i,
       "sex": Math.round(Math.random() * 10) % 2,
       "age": Math.round(Math.random() * 6) + 3
   });
}

根據姓名查一些特定的人

db.getCollection('grade_1_5').find(
    {
        $or: [{"name":"zhangsan10000"},{"name":"zhangsan14000"},{"name":"zhangsan9000"},{"name":"zhangsan23000"},{"name":"zhangsan24050"},
              {"name":"zhangsan12000"},{"name":"zhangsan14300"},{"name":"zhangsan9300"},{"name":"zhangsan23300"},{"name":"zhangsan24350"},
              {"name":"zhangsan11100"},{"name":"zhangsan15200"},{"name":"zhangsan8100"},{"name":"zhangsan22100"},{"name":"zhangsan26150"},
              {"name":"zhangsan10200"},{"name":"zhangsan14020"},{"name":"zhangsan9020"},{"name":"zhangsan23020"},{"name":"zhangsan24070"},
              {"name":"zhangsan10300"},{"name":"zhangsan14030"},{"name":"zhangsan9030"},{"name":"zhangsan23030"},{"name":"zhangsan24080"}]
    }
)

對姓名創建索引,會佔用5873664字節

db.getCollection('grade_1_5').ensureIndex({"name": 1})

對於上面的命令,經過調整順序,觀察時間,對性能有一些大概的瞭解;

行爲 建立-查詢-建索引 建立-建索引-查詢 建索引-建立-查詢
建立時間 150.957s 150.957s 159.967s
查詢時間 1.024s 0.005s 0.005s
創建索引時間 0.527s 0.527s 0.009s

遊標

直接對一個集合調用find()方法時,咱們會發現,若是查詢結果超過二十條,只會返回二十條的結果,這是由於Mongodb會自動遞歸find()返回的是遊標。

var cursor = db.getCollection('grade_1_4').find({});

執行上述命令時,shell並不會真正地訪問數據庫,而是等待開始要求得到結果的時候才向數據庫發送查詢請求。

此時能夠對這個遊標進行各類設置,而後調用遊標的hashNext()next()方法,這樣就會真正訪問數據庫,這是一個懶加載的過程,以下

var cursor = db.getCollection('grade_1_4').find({});

while(cursor.hasNext()){  
    var doc = cursor.next();  
    // do stuff with doc  
};

能夠基於遊標,進行limit、skip、sort操做,通常應用於分頁場景;

  • limit:限制遊標返回的數量,指定了上限;
  • skip :忽略前面的部分文檔,若是文檔總數量小於忽略的數量,則返回空集合;
  • sort :對獲得的子集合進行排序,能夠按照多個鍵進行正反排序;

上述三個命令能夠進行鏈式操做;

附錄

如下是文章會用到的參考,及有意義的擴展閱讀;

相關文章
相關標籤/搜索