Optimizing A Simple ExampleThis section describes proper techniques for optimizing database performance. Let's consider an example. Suppose our task is to display the front page of a blog - we wish to display headlines of the 10 most recent posts. Let's assume the posts have a timestamp field ts. The simplest thing we could write might be: 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());
}
Optimization #1: Create an indexOur first optimization should be to create an index on the key that is being used for the sorting: db.posts.ensureIndex({ts:1});
With an index, the database is able to sort based on index information, rather than having to check each document in the collection directly. This is much faster. Optimization #2: Limit resultsMongoDB cursors return results in groups of documents that we'll call 'chunks'. The chunk returned might contain more than 10 objects - in some cases, much more. These extra objects are a waste of network transmission and resources both on the app server and the database. As we know how many results we want, and that we do not want all the results, we can use the limit() method for our second optimization. articles = db.posts.find().sort({ts:-1}).limit(10); // 10 results maximum
Now, we'll only get 10 results returned to client. Optimization #3: Select only relevant fieldsThe blog post object may be very large, with the post text and comments embedded. Much better performance will be achieved by selecting only the fields we need: articles = db.posts.find({}, {ts:1,title:1,author:1,abstract:1}).sort({ts:-1}).limit(10);
articles.forEach( function(post) { print(post.getSummary()); } );
The above code assumes that the getSummary() method only references the fields listed in the find() method. Note if you fetch only select fields, you have a partial object. An object in that form cannot be updated back to the database: a_post = db.posts.findOne({}, Post.summaryFields);
a_post.x = 3;
db.posts.save(a_post); // error, exception thrown
Using the ProfilerMongoDB includes a database profiler which shows performance characteristics of each operation against the database. Using the profiler you can find queries (and write operations) which are slower than they should be; use this information, for example, to determine when an index is needed. See the [Performance Tuning] section of the Mongo Developers' Guide for more information. Optimizing Statements that Use count()To speed operations that rely on count(), create an index on the field involved in the count query expression. db.posts.ensureIndex({author:1});
db.posts.find({author:"george"}).count();
Increment OperationsMongoDB supports simple object field increment operations; basically, this is an operation indicating "increment this field in this document at the server". This can be much faster than fetching the document, updating the field, and then saving it back to the server and are particularly useful for implementing real time counters. See the Updates section of the Mongo Developers' Guide for more information. Circular Fixed Size CollectionsMongoDB provides a special circular collection type that is pre-allocated at a specific size. These collections keep the items within well-ordered even without an index, and provide very high-speed writes and reads to the collection. Originally designed for keeping log files - log events are stored in the database in a circular fixed size collection - there are many uses for this feature. See the Capped Collections section of the Mongo Developers' Guide for more information. Server Side Code ExecutionOccasionally, for maximal performance, you may wish to perform an operation in process on the database server to eliminate client/server network turnarounds. These operations are covered in the Server-Side Processing section of the Mongo Developers' Guide. ExplainA great way to get more information on the performance of your database queries is to use the $explain feature. This will display "explain plan" type info about a query from the database. When using the mongo - The Interactive Shell, you can find out this "explain plan" via the explain() function called on a cursor. The result will be a document that contains the "explain plan". db.collection.find(query).explain(); provides information such as the following: {
"cursor" : "BasicCursor",
"indexBounds" : [ ],
"nscanned" : 57594,
"nscannedObjects" : 57594,
"n" : 3 ,
"millis" : 108
}
This will tell you the type of cursor used (BtreeCursor is another type – which will include a lower & upper bound), the number of records the DB had to examine as part of this query, the number of records returned by the query, and the time in milliseconds the query took to execute. HintWhile the mongo query optimizer often performs very well, explicit "hints" can be used to force mongo to use a specified index, potentially improving performance in some situations. When you have a collection indexed and are querying on multiple fields (and some of those fields are indexed), pass the indexe as a hint to the query. You can do this in two different ways. You may either set it per query, or set it for the entire collection. To set the hint for a particular query, call the hint() function on the cursor before accessing any data, and specify a document with the key to be used in the query: db.collection.find({user:u, foo:d}).hint({user:1});
To force the query optimizer to not use indexes (do a table scan), use: > db.collection.find().hint({$natural:1})
See Also |

PLEASE POST QUESTIONS IN THE USER GROUPS FORUM. Post non-question comments and helpful hints here.
blog comments powered by Disqus