MongoDB and Morphia Performance

Internally we've been looking at using MongoDB for a number of projects. Of course one of the areas of concern when moving to to a NoSQL data store is performance and accessibility.

To this end, I started looking at how best to persist our Java Objects in MongoDB. There were a number of options to consider:

  • Hand crafted save, update, find and delete per Object class
  • Extending a class which contained the above per Object class
  • Generalisation of serialisation via an ORM

Each had negatives and positives, but the major one for us was performance. The application we had in mind would generate thousands of requests per minutes (maybe ultimately per second), and we needed something that could handle the scale, which still providing a level of abstraction. You can see our previous performance testing of MongoDb vs MySql - Fight !!. The aim of this test was to take the usefulness of MongoDB to the next level.

Testing Approach

It's worth mentioning a brief overview of the testing approach. This testing is exhaustive, it's not even particularly scientific. Each timing tests used System.nanoTime() to measure the time it took to create, convert and insert the record into MongoDB. The Database Connection and Collection setup was excluded from the testing.

Each test was run 100 times and an aggregate time was taken. The Collection in MongoDB was deleted after each run.

Morphia - A drug too good to be true?

One of the key serialisation technologies for MongoDB at present is Morphia. It's an extremely simple and lightweight ORM, which is actioned through the annotation of existing classes. You can find more information on Morphia [http://code.google.com/p/morphia/].

For my test I used a simple class called Number.

public class Number {
 
	private String number;
	private int networkId;
	.
	.
	.
	// getters and setters()
}

The first thing to do was implement the annotations. In Morphia, this meant annotation the class, and adding a few additional MongoDB-specific properties. Here's the finished product.

import com.google.code.morphia.annotations.MongoDocument;
import com.google.code.morphia.annotations.MongoCollectionName;
import com.google.code.morphia.annotations.MongoID;
 
@MongoDocument
public class Number {
	@MongoID 
	private String id;
 
	@MongoCollectionName 
	private String collectionName;
 
	private String number;
	private String key;
	private int networkId;
	.
	.
	.
	// getters and setters()
}

The id property is added by Morphia when the record is saved to the database, and the collectionName is used when you create the document. Now my class was annotated it was time to get it into the database.

This was done very simply by initialising the Morphia system within the code, adding a mapping to my object, and then saving an object. The following code shows this process.

	// Setup our MongoDB Connection
	Mongo m = new Mongo();
	DBCollection coll = db.getCollection("NumberSet");
 
	// Configure Morphia
	Morphia morphia = new Morphia();
	morphia.map(Number.class);
 
	// Create our Java Object
	Number number = new Number();
	number.setNetworkId(85324);
	number.setNumber("44777000001");
	number.setKey("44777000001");
 
	// Serialize and save the record to MongoDB
	DBObject numberDbObj = morphia.toDBObject(number);
	coll.save(numberDbObj);

To aid in the performance comparison, I've added the baseline of our original MongoDB testing. This was based on inserting the same effective class as above, but using the MongoDB BasicDBObject directly, instead of serialisation.

Test Records Speed
BasicDBObject (Insert) 10,000 877.76
Morphia (Class with Get/Set) 10,000 2113.11

This was something of a disappointment. Although I was expecting some overhead, 2.5x was more than anticipated.

Try again

So we started to think where the overhead might be. Was it in the creation of the Java Object (the getters/setters), or the serialisation. To that end, we tried a new class named NumberStatic. This only had public properties (no getters/setters). The following example shows this.

	// Setup our MongoDB Connection
	Mongo m = new Mongo();
	DBCollection coll = db.getCollection("NumberSet");
 
	NumberStatic number = new NumberStatic();
	number.networkId = 85324;
	number.number = "44777000001";
	number.key ="44777000001";
 
	// Serialize and save the record to MongoDB
	DBObject numberDbObj = morphia.toDBObject(number);
	coll.save(numberDbObj);

So where does this get us?

Test Records Speed
BasicDBObject (Insert) 10,000 877.76
Morphia (Class with Get/Set) 10,000 2113.11
Morphia (Class with Public Properties) 10,000 2368.37

Oh well. So much for that idea.

Class getBasicDBObject

What if we were to make the class responsible for creating the MongoDB BasicDBObject? Good question, lets try it.

	import com.mongodb.BasicDBObject;
 
	public class NumberDAO {
 
	private String number;
	private String key;
	private int networkId;
	.
	.
	. // Get/Set
 
	public BasicDBObject getBasicDBObject()
	{
		BasicDBObject num = new BasicDBObject();
		num.put("number", this.number );
		num.put("key", this.key); 
		num.put("networkId", this.networkId); 
		return num;
	}	
}

Abracadabra…

Test Records Speed
Straight BasicDBObject (Insert) 10,000 877.76
Morphia (Class with Get/Set) 10,000 2113.11
Morphia (Class with Public Properties) 10,000 2368.37
Class emits BasicDBObject (get/set) 10,000 824.41
Class emits BasicDBObject (public properties) 10,000 816.07

Conclusion

Obviously, this doesn't cover all the others aspects of the data manipulation we need to perform for a functioning system. However, based on the above we can assume that handling a lot of the serialisation within our classes ourselves is going to give us a performance boost.

Developments within the MongoDB sphere such as Morphia are fantastic. They help to bring talent and support to a growing project. If your own application isn't so time-critical, Morphia is a fantastic choice. It's super-simple to implement, transparent and has a ton of additional features I couldn't cover here.

For our needs, ORM's (at least within MongoDB) aren't quite ready for prime time, but we'll be keeping a close eye on them going forward.

Feedback and comments welcomed : steven.algieri@scalabiliti.com