Mongodb索引

1、索引基礎:
    MongoDB的索引幾乎與傳統的關係型數據庫如出一轍,這其中也包括一些基本的優化技巧。下面是建立索引的命令:
    > db.test.ensureIndex({"username":1})
    能夠經過下面的名稱查看索引是否已經成功創建:
    > db.test.getIndexes()
    刪除索引的命令是:
    > db.test.dropIndex({"username":1})
    在MongoDB中,咱們一樣能夠建立複合索引,如:
    -- 數字1表示username鍵的索引按升序存儲,-1表示age鍵的索引按照降序方式存儲。
    > db.test.ensureIndex({"username":1, "age":-1})
    該索引被建立後,基於username和age的查詢將會用到該索引,或者是基於username的查詢也會用到該索引,可是隻是基於age的查詢將不會用到該複合索引。所以能夠說,若是想用到複合索引,必須在查詢條件中包含複合索引中的前N個索引列。然而若是查詢條件中的鍵值順序和複合索引中的建立順序不一致的話,MongoDB能夠智能的幫助咱們調整該順序,以便使複合索引能夠爲查詢所用。如:
    > db.test.find({"age": 30, "username": "stephen"})
    對於上面示例中的查詢條件,MongoDB在檢索以前將會動態的調整查詢條件文檔的順序,以使該查詢能夠用到剛剛建立的複合索引。
    咱們能夠爲內嵌文檔建立索引,其規則和普通文檔沒有任何差異,如:
    > db.test.ensureIndex({"comments.date":1})
    對於上面建立的索引,MongoDB都會根據索引的keyname和索引方向爲新建立的索引自動分配一個索引名,下面的命令能夠在建立索引時爲其指定索引名,如:
    > db.test.ensureIndex({"username":1},{"name":"testindex"})   
    隨着集合的增加,須要針對查詢中大量的排序作索引。若是沒有對索引的鍵調用sort,MongoDB須要將全部數據提取到內存並排序。所以在作無索引排序時,若是數據量過大以至沒法在內存中進行排序,此時MongoDB將會報錯。
   
2、惟一索引:
    在缺省狀況下建立的索引均不是惟一索引。下面的示例將建立惟一索引,如:
    > db.test.ensureIndex({"userid":1},{"unique":true})
    若是再次插入userid重複的文檔時,MongoDB將報錯,以提示插入重複鍵,如:
    > db.test.insert({"userid":5})
    > db.test.insert({"userid":5})
    E11000 duplicate key error index: test.test.$userid_1  dup key: { : 5.0 }   
    若是插入的文檔中不包含userid鍵,那麼該文檔中該鍵的值爲null,若是屢次插入相似的文檔,MongoDB將會報出一樣的錯誤,如:
    > db.test.insert({"userid1":5})
    > db.test.insert({"userid1":5})
    E11000 duplicate key error index: test.test.$userid_1  dup key: { : null }       
    若是在建立惟一索引時已經存在了重複項,咱們能夠經過下面的命令幫助咱們在建立惟一索引時消除重複文檔,僅保留髮現的第一個文檔,如:
    --先刪除剛剛建立的惟一索引。
    > db.test.dropIndex({"userid":1})
    --插入測試數據,以保證集合中有重複鍵存在。
    > db.test.remove()
    > db.test.insert({"userid":5})
    > db.test.insert({"userid":5})   
    --建立惟一索引,並消除重複數據。
    > db.test.ensureIndex({"userid":1},{"unique":true,"dropDups":true})   
    --查詢結果確認,重複的鍵確實在建立索引時已經被刪除。
    > db.test.find()
    { "_id" : ObjectId("4fe823c180144abd15acd52e"), "userid" : 5 }   
   
    咱們一樣能夠建立複合惟一索引,即保證複合鍵值惟一便可。如:
    > db.test.ensureIndex({"userid":1,"age":1},{"unique":true})   
   
3、使用explain:
    explain是很是有用的工具,會幫助你得到查詢方面諸多有用的信息。只要對遊標調用該方法,就能夠獲得查詢細節。explain會返回一個文檔,而不是遊標自己。如:
    > db.test.find().explain()
    {
        "cursor" : "BasicCursor",
        "nscanned" : 1,
        "nscannedObjects" : 1,
        "n" : 1,
        "millis" : 0,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "isMultiKey" : false,
        "indexOnly" : false,
        "indexBounds" : {

        }   
    }
    explain會返回查詢使用的索引狀況,耗時和掃描文檔數的統計信息。
    "cursor":"BasicCursor"表示沒有使用索引。
    "nscanned":1 表示查詢了多少個文檔。
    "n":1 表示返回的文檔數量。
    "millis":0 表示整個查詢的耗時。
mongodb的是B-tree的索引了。要注意的是,一個collection不能超過64個索引, 
索引的大小不能超過1024字節,其中包括字段名和值和命名空間。
   首先照樣建立數據:
db.Indexing.insert( { name : "Denis", age : 20 } )
db.Indexing.insert( { name : "Abe", age : 30 } )
db.Indexing.insert( { name : "John", age : 40 } )
db.Indexing.insert( { name : "Xavier", age : 10 } )
db.Indexing.insert( { name : "Zen", age : 50 } )

  首先,嘗試看下mongodb的執行計劃:
db.Indexing.find({name: "Denis"}).explain(),這個是看當查找Denis的執行狀況,
結果以下:
  {
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 0,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 0,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {

        },
        "server" : "Denis:27017"
}
 
下面加個索引,以下:
   db.Indexing.ensureIndex({name: 1});
  其中依然,1是升序,-1是降序,再看下執行計劃:

db.Indexing.find({name: "Denis"}).explain()
結果爲:

  {
        "cursor" : "BtreeCursor name_1",
        "isMultiKey" : false,
        "n" : 1,
        "nscannedObjects" : 1,
        "nscanned" : 1,
        "nscannedObjectsAllPlans" : 1,
        "nscannedAllPlans" : 1,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 1,
        "indexBounds" : {
                "name" : [
                        [
                                "Denis",
                                "Denis"
                        ]
                ]
        },
        "server" : "Denis:27017"
}
   能夠看到,"cursor" 一欄中,已經變成了btree了;而且"indexBounds" :中如今有內容了。
  而後能夠刪除索引:
  db.Indexing.dropIndex({name: 1});
  刪除全部索引 

4、索引管理:
    system.indexes集合中包含了每一個索引的詳細信息,所以能夠經過下面的命令查詢已經存在的索引,如:
    > db.system.indexes.find()
    若是在爲已有數據的文檔建立索引時,能夠執行下面的命令,以使MongoDB在後臺建立索引,這樣的建立時就不會阻塞其餘操做。可是相比而言,以阻塞方式建立索引,會使整個建立過程效率更高,可是在建立時MongoDB將沒法接收其餘的操做。
    > db.test.ensureIndex({"username":1},{"background":true})
mongodb

 

關於索引的建議數據庫

咱們收到了不少關於索引的問題。這一部分解答了其中的一小部分。有幾點要記住。服務器

第一,MongoDB索引和MySQL索引很是類似而且對於MySQL的索引優化有不少也適用於MongoDB。工具

第二,更重要的是,這些索引的建議對你的應用提升也是有限的。post

對於應用的最佳索引策略應該基於不少的重要因素。包含了你指望查詢的類型,性能

數據讀取與寫入的比率,甚至於你服務器的空閒內存。意思就是,測試

須要對線上的產品作不少的測試剖析工做,才能調整出最佳的索引策略。優化

沒有什麼好的方法能夠替代實際經驗的。spa

索引策略orm

下面有些索引的基本法則

建立的索引要匹配查詢。

若是你僅僅要查詢單個字段。索引這個字段便可。如

db.posts.find({ slug : 'state-of-mongodb-2010' })

這個例子中,惟一索引是最佳的

db.posts.ensureIndex({ slug: 1 }, {unique: true});

然而,通常都查詢多個鍵而且排序結果。這種狀況,組合索引是最佳的,例子以下

db.comments.find({ tags : 'mongodb'}).sort({ created_at : -1 });

建立的索引以下

db.comments.ensureIndex({tags : 1, created_at : -1});

要注意的是若是咱們按照升序排序created_at。索引效率就低下了。

每一個查詢一個索引。

有的時候查詢多個鍵,須要多個索引。在MongoDB中,這麼作沒問題。

若是你有個查詢要匹配多個鍵,而且你想更有效地使用索引,請使用組合索引。

要肯定全部的索引都在RAM中。

Shell中提供了一個查看索引大小的命令,以下:

db.comments.totalIndexSize();
65443

若是你的查詢有點遲緩,你應該查看下索引是否都存入到RAM中了。

一個實例,若是你運行在4GB的RAM機器而且有3GB的索引,那麼索引可能並不能全存在RAM中。

你須要添加RAM以及/或者校驗實際的索引使用量。

要當心單鍵索引的低選擇性。

假使你有個字段叫作'status',就有兩個值new和processed。

若是在status上建立索引那麼這就是個低選擇性的索引。

意味着,查詢中沒有什麼優點而且還佔大量的空間。

一個更好一點的策略,固然依賴具體查詢需求,能夠建立組合索引包括這個低選擇性的字段。

舉例來講,你能夠建立一個組合索引在status和created_at字段上。

另外一個選擇,固然也依賴你的需求,能夠分離collection, 一個狀態一個。

固然這些建議必定要進行測試,選擇最優的方案。

使用explain.

MongoDB提供了一個explain命令,

用來查看查詢的過程而且能夠查看是否使用縮影。explain能夠在驅動中使用,也能夠在SHELL中使用:

 

db.comments.find({ tags : 'mongodb'}).sort({ created_at : -1 }).explain();

 

返回了不少有用的信息。包含了檢索的條數,消耗的毫秒數,優化器嘗試的索引以及最終所使用的索引。

若是你歷來沒有使用過explain,請開始使用吧。

理解explain的 輸出.

explain輸出主要有三個字段:

  • cursor: 遊標不是 BasicCursor就是 BtreeCursor. 第二種意味着使用了索引。
  • nscanned: 掃描document的行數。
  • n: 查詢返回的行數。你但願n的值和nsanned值接近。要避免作collection的掃描,
  • 也就是訪問全部的document。
  • millis: 查詢完成的毫秒數。這個對於比較索引和非索引性能很是有用。

要關注應用讀/寫( read/write) 比率

這個很重要,由於,添加索引意味着添加,更新,刪除操做變慢。

若是你的應用是偏向於讀取,使用索引是很是好的事情。

可是若是你的應用偏向於寫,那麼建立索引就要當心了。增長索引都很影響寫入的性能。

通常來講, 不要隨便添加索引。索引應該按照你的查詢來添加。

添加索引的理由老是不少的, 以及要進行大量的測試選擇合適的索引策略。

索引特性

組合索引有許多特性要記住。

下面的例子都假想在 a, b, c上建立組合索引。所以建立索引語句以下

db.foo.ensureIndex({a: 1, b: 1, c: 1})

1. 排序的列必定要在索引的最後。

好的:

  • find(a=1).sort(a)
  • find(a=1).sort(b)
  • find(a=1, b=2).sort(c)

很差的:

 

  • find(a=1).sort(c)
  • 即便c是索引的最後一列,a列是所使用的最後一列,所以你只能經過a或者b列進行排序。

 5. MongoDB's $ne 或者$nin 操做在索因傷是無效的。

當要排除不多的documents。最好的方法就是在MongoDB查詢出結果,在服務端進行排除。

相關文章
相關標籤/搜索