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:
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.
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.
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.
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.
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 |
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