首先,mongodb中的索引同MySQL中的很類似,因此很多在MySQL中建立高效索引的技術也適用於mongodb。
再者,而且可能更加重要的是,索引最佳化建議也只能到此為止。對於你的應用,最好的索引依賴於幾個重要的因素,包含你期望的查詢種類,讀/寫頻率,甚至系統的剩餘記憶體。這意味著最好的策略就是對資料集準備多套索引配置方案,然後觀察那個表現最好。
索引策略
這裡有一些建立良好索引的基本原則。
建立匹配查詢的索引
如果你僅對一個關鍵字查詢,那麼使用單鍵索引就可以了。例如,你可能正在搜尋部落格發布的緩動環(slug):
db.posts.find({ slug : 'state-of-mongodb-2010' })
在這種情景下,基於單個關鍵字的唯一索引是最好的:
db.posts.ensureIndex({ slug: 1 }, {unique: true});
但是,對多個關鍵字進行查詢並排序也是很普遍的。在這些時候,複合索引是最合適的。這裡有個例子是中查詢包含最近新加的名為“mongodb”標籤的評論:
db.comments.find({ tags : 'mongodb'}).sort({ created_at : -1 });
那麼合適的索引是:
db.comments.ensureIndex({tags : 1, created_at : -1});
需要注意的是,如果我們期望對"created_at"進行升序排序,那麼這個索引的效率會很低。
一個查詢一個索引
有時候我們會認為對多個關鍵字的查詢會使用多個索引;在Mongodb中不是這樣的。如果你的查詢是針對多個關鍵字的,並且你希望提高查詢的效率,那麼建立一個複合索引是很有必要的。
確保你的索引可以駐留在記憶體中
shell提供了一個命令查詢某個集合的索引大小:
db.comments.totalIndexSize();
65443
留意低效率的單鍵索引
假定你有一個欄位名為“status”,它的值為“new”或者“processeed”。如果你對它建立一個索引,其效率會很低,這意味著該索引在定位記錄上協助不大並且可能會佔用很多空間。
一個更好的方法,當然依賴於你的查詢,建立包含低效率欄位的複合索引。例如,你可以對"status"和"created_at"建立複合索引。
另一個選擇,同樣也依賴於你的用例,可以使用分開的集合,為每一種狀態建立一個集合。已經有了這麼多的建議,實驗和基準測試可以幫你選擇最好的。
使用explain
Mongodb有一個explain命令查看你的查詢如何被執行,特別是有沒有使用到一個索引。
可以在驅動中使用explain,也可以在shell中:
db.comments.find({ tags : 'mongodb'}).sort({ created_at : -1 }).explain();
這會返回很多有用的資訊,包含掃描的對象個數,查詢耗費時間(單位毫秒),嘗試使用的索引,最終使用的索引。
如果你從來沒有使用過explain,那麼現在是時候了。
理解explain的輸出
這裡有explain命令輸出的三個主要欄位:
- cursor:cursor的值可以是BasicCursor或BtreeCursor。第二個值指明使用的索引。
- nscanned:掃描的文檔個數。
- n:查詢返回的文檔個數。你需要使得n與nscanned非常接近。需要避免的情況是,查詢掃描了集合中所有的文檔。這種情況下nscanned等於集合中文檔個數。
- millis:查詢耗費時間。
關注你的應用中讀/寫比率
它之所以重要,是因為當你添加一個索引,你就會對所有的插入、更新、刪除操作都額外增加了負擔。如果你的應用是讀繁忙類型,如多數web應用,增加的索引通常是個好東西。但是如果你的應用是寫繁忙類型的,增加索引時要特別小心,因為每個索引都會對寫操作增加一定的負擔。
通常情況下,不要害怕增加索引。索引通常應當增加以完成你的查詢。記住總是有一個好的理由添加一個新的索引,並確保你已經比較了替代策略。
索引特性
這些例子假定一個基於三個欄位的複合索引: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排序。
2. 範圍查詢也必須是索引的最後列。這是上面1中的一個原則。
好:
- find(a=1,b>2)
- find(a>1 and a<10)
- find(a>1 and a<10).sort(a)
不好:
3. 僅對一個列進行範圍查詢和排序
好:
- find(a=1,b=2).sort(c)
- find(a=1,b>2)
- find(a=1,b>2 and b<4)
- find(a=1,b>2).sort(b)
不好:
- find(a>1,b>2)
- find(a=1,b>2).sort(c)
4.通過對相等性(無範圍)查詢的列進行重排序來節約索引
假定你有兩個查詢:
- find(a=1,b=1,d=1)
- find(a=1,b=1,c=1,d=1)
一個基於a,b,c,d的單個索引就可以滿足這兩個查詢了。
如果,你需要對最後的值進行排序,你就可能需要2個索引了。
5. Mongodb的$ne和$nin操作符,使用索引是沒有效率的
- 當需要排序少量文檔時,最好將資料讀到用戶端後進行排除。