優(yōu)化一個簡單的例子
這部分主要講解如何優(yōu)化MongoDB的性能。
讓我們舉個具體示例。假使我們的任務(wù)是現(xiàn)實blog的首頁-我們希望現(xiàn)實最近發(fā)布的10條posts。ts為時間字段。
語句如下
articles = db.posts.find().sort({ts:-1});
// get blog posts in reverse time order
for (var i=0; i< 10; i++)
{ print(articles[i].getSummary());}
優(yōu)化 #1: 創(chuàng)建索引
第一個優(yōu)化就是要在ts上創(chuàng)建索引,用來快速排序。
db.posts.ensureIndex({ts:1});
使用索引,數(shù)據(jù)庫就可以基于索引信息排序,不會直接查看每個document。這樣做更快。
優(yōu)化#2: 限定結(jié)果
MongoDB游標(biāo)返回一組document,我們叫這個為chunks。
這chunk可能包含超過10個對象。額外的對象對于我們的需求是浪費,
浪費了網(wǎng)絡(luò)帶寬和應(yīng)用服務(wù)器以及數(shù)據(jù)庫的資源。
我們知道想要結(jié)果的個數(shù),那么就不需要所有的結(jié)果。我們可以使用limit()方法
articles = db.posts.find().sort({ts:-1}).limit(10);
// 最多10條
現(xiàn)在,我們從客戶端返回了10條。
優(yōu)化 #3: 查詢相關(guān)的字段
post對象非常大, 如post文本和評論數(shù)組。 比較好的方式是只查詢我們要用到的字段。
articles = db.posts.find({}, {ts:1,title:1,author:1,abstract:1}).sort({ts:-1}).limit(10);
articles.forEach( function(post) { print(post.getSummary()); } );
上面的getSummary()方法假使是可以獲得find()方法返回的字段值
注意,如果你選擇了要查詢的字段,那么返回的就是部分對象。這個對象并不能直接進(jìn)行更新。如下
a_post = db.posts.findOne({}, Post.summaryFields);
a_post.x = 3;
db.posts.save(a_post); // 錯誤,拋出異常
使用 Profiler
MongoDB有一個數(shù)據(jù)庫的 profiler,用來顯示每個操作的性能。
使用profiler你可以查看到哪些查詢或者寫入的速度比較慢。
舉個例子,使用這些信息可以知道什么時候需要索引。詳情查看 Database Profiler 。
Use count()優(yōu)化語句
加速語句速度依賴于count(),創(chuàng)建一個索引,調(diào)用count()。
db.posts.ensureIndex({author:1});
db.posts.find({author:"george"}).count();
增量操作Increment Operations
MongoDB 支持簡單對象字段的增量操作;
基本上來說, 這個操作就是 在服務(wù)器document中增量一個字段"。
這個要比"獲取一個document,更新這個字段并且在保存會服務(wù)器"這個方法快很多,
并且對于實時的計數(shù)器更為有用。詳情請看 Updates 。
固定大小的collection。
MongoDB提供了一個特殊的collection,它提前分配好了存儲空間。
保存的項都是固定順序的,并且沒有索引。而且寫入和讀取是非常高速的。
存儲是為了保存日志文件所設(shè)置的。詳情查看 Capped Collections
服務(wù)端代碼執(zhí)行Server Side Code Execution
也許有的時候為了高性能,避免客戶端和服務(wù)端來回通信,需要直接在服務(wù)端執(zhí)行代碼。
這部分查看 Server-Side Processing 。
Explain工具
要想查看查詢語句的詳細(xì)性能信息,最好的方法就是使用explain方法。
返回的結(jié)果就是整個查詢執(zhí)行的一些信息。
當(dāng)使用shell的時候,可以調(diào)用cursor的explain() 方法。
db.collection.find(query).explain();
返回的信息如下
{"cursor" : "BasicCursor",
"indexBounds" : [ ],
"nscanned" : 57594,
"nscannedObjects" : 57594,
"nYields" : 2 ,
"n" : 3 ,
"millis" : 108,
"indexOnly" : false}
現(xiàn)實結(jié)果可以得知cursor的類型,DB掃描的數(shù)據(jù)數(shù),返回的數(shù)據(jù)數(shù),還有執(zhí)行的毫秒數(shù)。
- nscanned - 掃描的數(shù)據(jù)條數(shù)。這個數(shù)據(jù)可能是對象也可能是索引的鍵。
- 如果"覆蓋索引(covered index)"被調(diào)用了,nscanned 要高于nscannedObjects.
- nscannedObjects - 掃描對象的數(shù)。
- nYields - 查詢所產(chǎn)生的鎖的個數(shù)。
- indexOnly - 是否使用了covered index。
Hint
雖然MongoDB查詢優(yōu)化器一般工作的很不錯,但是也可以使用hints來強(qiáng)迫MongoDB使用一個指定的索引。
這種方法某些情形下會提升性能。 一個有索引的collection并且執(zhí)行一個多字段的查詢(一些字段已經(jīng)索引了)。
傳入一個指定的索引,強(qiáng)迫查詢進(jìn)行使用。
db.collection.find({user:u, foo:d}).hint({user:1});
![]() | 確定創(chuàng)建了索引。 上面的例子,首先你確定索引已經(jīng)創(chuàng)建了。請使用ensureIndex()創(chuàng)建索引。 |
其他的例子,有個在 {a:1, b:1} 上的索引,名稱為"a_1_b_1":
db.collection.find({a:4,b:5,c:6}).hint({a:1,b:1});
db.collection.find({a:4,b:5,c:6}).hint("a_1_b_1");
強(qiáng)迫查詢不適用索引, (做一個表的掃描), 使用:
> db.collection.find().hint({$natural:1})