MongoDB, de database van de toekomst? De evolutie van databases

Big Data is al lang geen hype meer. Data groeit niet alleen wat betreft hoeveelheid, ook de structuur van opslaan verandert. Omdat de hoeveelheid data toeneemt, wordt cloud computing steeds vaker ingezet. Daarnaast moet data vanuit elke business geanalyseerd worden en het liefst realtime beschikbaar zijn. Bijvoorbeeld op dashboards op je eigen device (BYOD) zodat beleid eventueel snel kan worden aangepast.

We kennen allemaal het aloude relationele model, dat door veel productleveranciers is geïmplementeerd in een Relationeel DataBase Management System (RDBMS). Denk bijvoorbeeld aan de relationele databases van Oracle, Microsoft en IBM. Rond het jaar 2000 was er veel behoefte aan salesreporting, marketinginformatie etc. en waren de zogenaamde On Line Analytical Processing (OLAP) datawarehouses populair. Data werd op een andere manier gemodelleerd (stermodellen met dimensions en facts) en het redundant opslaan van data was toegestaan. Vanaf 2010 zijn NoSQL-databases steeds populairder geworden, wat niet betekent dat relationele databases en datawarehouses gaan verdwijnen. Ze zullen naast elkaar blijven bestaan. NoSQL database implementaties maken onder andere gebruik van andere vormen van opslag dan het klassieke relationele model.

NoSQL databases zijn er in diverse verschijningen, en in veel gevallen doen ze iets wat met relationele databases minder goed kan. Een van de meest populaire databases van de laatste tijd is MongoDB. We zullen in dit artikel verder op deze database ingaan. Wat maakt MongoDB interessant en wat betekent deze ontwikkeling voor de Java ontwikkelaar?

MongoDB en 10gen

MongoDB is een open source document oriented database geschreven in C++. De data is opgelagen als binairy JSON ook bekend als BSON. De eerste release is uitgebracht in 2009 en momenteel heeft v2.4 de productionele status. De database kan gemakkelijk gedistribueerd worden. De data wordt dan over meerdere computers verspreid om gedistribueerde gegevensverwerking mogelijk te maken. Er is geen ondersteuning voor joins en ondersteuning voor transacties is beperkt. MongoDB is beter geschikt voor Big Data dan een relationele database van Oracle, MySQL of Microsoft. Enkele belangrijke productionele omgevingen zijn Foursquare, bit.ly en CERN voor het verzamelen van grote hoeveelheden data. De belangrijkste features op een rij:

 

  • Document data model with dynamic schemas
  • Full, flexible index support and rich queries
  • Auto-Sharding for horizontal scalability
  • Built-in replication for high availability
  • Text search
  • Advanced security
  • Aggregation Framework and MapReduce
  • Large media storage with GridFS

 

10gen is het bedrijf achter MongoDB en begeleidt de MongoDB ontwikkeling, ondersteunt de grote en groeiende MongoDB community, biedt commerciële abonnementen inclusief ondersteuning, advies en training. Daarnaast biedt 10gen het gratis, cloud-gebaseerde MongoDB Monitoring Service (MMS).

Installeren van MongoDB

Je kan MongoDB installeren op verschillende platformen, op zowel 32 bits als 64 bits architecturen. Zie voor meer informatie op http://docs.mongodb.org/manual/installation/

CRUD CV Database

We maken een simple CV database aan, waar we personen en expertisegroepen via CRUD functionaliteit kunnen beheren. Log aan in de MongoDB shell met “./mongo”

 


> use cv
switched to db cv

 

Insert

Op het moment van een insert, is de database CV ook echt aangemaakt:

 


> db.person.insert(
{
 "name" : "Bas",
 "group" : [ "Open Source", "mongoDB", "Big Data" ],
 } )

 

en nog 1 toevoegen:

 


> db.person.insert(
 {
 "name" : "Maikel",
 "group" : [ "Oracle", "ExaData", "Big Data"],
 } )

 

In MongoDB wordt “person” een collectie genoemd, en de rijen worden documents genoemd. We kunnen nu het aantal documents tellen:

 


> db.person.count()
2

 

Retrieve

Met behulp van het find commando kunnen we het document opvragen:

 


> db.person.find()
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "name" : "Bas", "group" : [ "Open Source", "mongoDB", "Big Data" ] }
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "name" : "Maikel", "group" : [ "Oracle", "ExaData", "Big Data" ] }

 

Wat nu opvalt is het veld “_id” van het type ObjectId. Een Objectid is een 12-byte BSON type, deze fungeert als primaire sleutel en is opgebouwd uit:

een 4-byte tijdstempel,

een 3-byte machine-id,

een 2-byte proces-ID en

een 3-byte teller, die gevuld wordt met een willekeurige waarde.

 

We kunnen nu ook documenten selectief selecteren. Geef alle documenten met een groep “Oracle”.

 


> db.person.find({"group" : "Oracle" })
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "name" : "Maikel", "group" : [ "Oracle", "ExaData", "Big Data" ] }

 

Update

Stel we besluiten het veld “group” te verwijderen bij het document met de naam “Maikel”

 


> db.person.update({name: "Maikel"}, {$unset: {group: 1}})
> db.person.find()
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "name" : "Bas", "group" : [ "Open Source", "mongoDB", "Big Data" ] }
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "name" : "Maikel" }

 

Of we vervangen alle group items bij het document met name “Bas”


> db.person.update({name: "Bas" },{ $set: {"group" : ["EOSS", "SQL", "OpenStack"]}})
> db.person.find()
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "group" : [ "EOSS", "SQL", "OpenStack" ], "name" : "Bas" }
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "name" : "Maikel" }

 

Of we voegen een group item “Oracle” toe, bij het document met de naam “Bas”

 


> db.person.update({name: "Bas" },{ $push: { group: "Oracle" } })
> db.person.find()
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "name" : "Maikel" }
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "group" : [ "EOSS", "SQL", "OpenStack", "Oracle" ], "name" : "Bas" }

 

Upsert

Een upsert is een speciaal type update. Als er geen document wordt gevonden dat aan de update criteria voldoet, zal een nieuw document worden gecreëerd. Als er een overeenkomend document wordt gevonden, zal het op de normale manier worden ge-update. Voor het gebruik van Upsert voeg je de parameter “true” toe.

 


> db.person.update({name: "Jan" },{ $set: {"group" : ["OpenStack"]}},true)
> db.person.find()
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "name" : "Maikel" }
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "group" : [ "EOSS", "SQL", "OpenStack", "Oracle" ], "name" : "Bas" }
{ "_id" : ObjectId("5145c1fbcb8282695dbab2af"), "group" : [ "OpenStack" ], "name" : "Jan" }

 

Multiple update

Ook hier geldt het toevoegen van de parameter “true”.

 


> db.person.update( { }, { $set: {"group" : ["EOSS", "mongoDB", "OpenStack"]}}, true, true )
> db.person.find()
{ "_id" : ObjectId("5145ae14d6edf3d8f2d67109"), "group" : [ "EOSS", "mongoDB", "OpenStack" ], "name" : "Maikel" }
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "group" : [ "EOSS", "mongoDB", "OpenStack" ], "name" : "Bas" }
{ "_id" : ObjectId("5145c1fbcb8282695dbab2af"), "group" : [ "EOSS", "mongoDB", "OpenStack" ], "name" : "Jan" }

 

Delete

Stel we willen het document met de naam “Maikel” verwijderen:

 


> db.person.remove({"name": "Maikel"})
> db.person.find()
{ "_id" : ObjectId("5145adfcd6edf3d8f2d67108"), "group" : [ "EOSS", "mongoDB", "OpenStack" ], "name" : "Bas" }
{ "_id" : ObjectId("5145c1fbcb8282695dbab2af"), "group" : [ "EOSS", "mongoDB", "OpenStack" ], "name" : "Jan" }

 

Stel we willen de hele collectie “person” verwijderen:


 
> db.person.drop()

Replicatie

Replicatie is zeer eenvoudig op te zetten met MongoDB. We gaan een primary (node1) en secundary (node2) instance aanmaken, met een aparte arbiter instance.

Een arbiter bepaalt de nieuwe primary in geval deze er niet meer is. Start met aanmaken van de data directories:

 


mkdir /data/node1
mkdir /data/node2
mkdir /data/arbiter

 

Start nu elke instance op in een aparte terminalsessie:


 
mongod --replSet person --dbpath /data/node1 --port 40001
mongod --replSet person --dbpath /data/node2 --port 40002
mongod --replSet person --dbpath /data/arbiter --port 40003

 

Log in op de primary instance om de replica set yet te configuren. Het eerste wat je ziet na het rs.initiate() commando is een foutmelding. RS staat voor replica set.

 


mongo localhost:40001
MongoDB shell version: 2.2.3
connecting to: localhost:40001/test
 
> rs.initiate()
{
"info2" : "no configuration explicitly specified -- making one",
"me" : "Computername.local:40001",
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}

 

Start nu de configuratie door de secundary en de arbiter te koppelen.

 


person:PRIMARY> rs.add(Computername:40002)
{ "ok" : 1 }
person:PRIMARY> rs.add(Computername:40003, {arbiterOnly:true})
{ "ok" : 1 }

 

Kijk nu met behulp van het rs.status() commando. De primary, secundary en de arbiter zijn opgezet:

 


person:PRIMARY> rs.status()
{
 "set" : "person",
 "date" : ISODate("2012-10-28T19:50:52Z"),
 "myState" : 1,
 "members" : [
 {
 "_id" : 0,
 "name" : "Computername.local:40001",
 "health" : 1,
 "state" : 1,
 "stateStr" : "PRIMARY",
 "uptime" : 1266,
 "optime" : Timestamp(1351453811000, 1),
 "optimeDate" : ISODate("2012-10-28T19:50:11Z"),
 "self" : true
 },
 {
 "_id" : 1,
 "name" : "Computername.local:40002",
 "health" : 1,
 "state" : 2,
 "stateStr" : "SECONDARY",
 "uptime" : 41,
 "optime" : Timestamp(1351453811000, 1),
 "optimeDate" : ISODate("2012-10-28T19:50:11Z"),
 "lastHeartbeat" : ISODate("2012-10-28T19:50:51Z"),
 "pingMs" : 0
 }
 ],
 "ok" : 1
}
{
 "_id" : 1,
 "name" : "Computername.local:40003",
 "health" : 1,
 "state" : 3,
 "stateStr" : "ARBITER",
 "uptime" : 14,
 "optime" : Timestamp(1351453811000, 1),
 "optimeDate" : ISODate("2012-10-28T19:50:11Z"),
 "lastHeartbeat" : ISODate("2012-10-28T19:50:51Z"),
 "pingMs" : 0
 }
 ],
 "ok" : 1
}

 

Het is nu tijd om een test te doen door middel van het toevoegen van een document in de primary instance:

 


person:PRIMARY> use cv
switched to db cv
person:PRIMARY> db.person.save(
{
"name" : "Maikel",
"group" : [ "Oracle", "ExaData", "Big Data"],
} )

 

Log in op de secundary en gebruik nog wel het rs.slaveOk() command om aan te geven dat lezen mogelijk is. Test of de collection en het document ook hier bestaan:

 


mongo localhost:40002
MongoDB shell version: 2.2.3
connecting to: localhost:40002/test
person:SECONDARY> rs.slaveOk()
person:SECONDARY> use cv
switched to db cv
person:SECONDARY> db.person.find()
{ "_id" : ObjectId("508d971dda0730903bcbb612"), "name" : "Maikel", "group" : [ "Oracle", "ExaData", "Big Data" ] }
person:SECONDARY>

 

We gaan nu een scriptje maken dat 1000000 documents (persons) genereert:

 


person:PRIMARY> for(i=0; i<1000000; i++) { db.person.save({person: i}); }

 

Log aan op de secundary en check of de data ook hier binnenkomt:

 


person:SECONDARY> db.person.find()
 { "_id" : ObjectId("508f95e9e38917f43ae20db3"), "person" : 0 }
 { "_id" : ObjectId("508f95e9e38917f43ae20db4"), "person" : 1 }
 { "_id" : ObjectId("508f95e9e38917f43ae20db5"), "person" : 2 }
 { "_id" : ObjectId("508f95e9e38917f43ae20db6"), "person" : 3 }
 { "_id" : ObjectId("508f95e9e38917f43ae20db7"), "person" : 4 }
 { "_id" : ObjectId("508f95e9e38917f43ae20db8"), "person" : 5 }
 { "_id" : ObjectId("508f95e9e38917f43ae20db9"), "person" : 6 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dba"), "person" : 7 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dbb"), "person" : 8 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dbc"), "person" : 9 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dbd"), "person" : 10 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dbe"), "person" : 11 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dbf"), "person" : 12 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc0"), "person" : 13 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc1"), "person" : 14 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc2"), "person" : 15 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc3"), "person" : 16 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc4"), "person" : 17 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc5"), "person" : 18 }
 { "_id" : ObjectId("508f95e9e38917f43ae20dc6"), "person" : 19 }
 Type "it" for more
 person:SECONDARY> db.person.count()
 194079
 person:SECONDARY> db.person.count()
 215657
 person:SECONDARY> db.person.count()
 228488
 person:SECONDARY> db.person.count()
 239528
 person:SECONDARY>

 

Het werkt!

Download nu de nieuwste Java driver voor MongoDB van: https://github.com/mongodb/mongo-java-driver/downloads

Maak een nieuw Java project aan in Eclipse of een andere IDE. Voeg de gedownloade JAR file toe in het build path. Gebruik de volgende code om je main class op te zetten.

 


package nl.nljug;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import com.mongodb.*;
 /**
 * Simple Person Report finding persons in a particular group.
 *
 */
public class PersonReport {
 /**
 * Find a persons which participating in a special interest group.
 * @param groupName
 * @param personCollection
 * @return cursor to persons found
 */
public DBCursor getPersonWithGroup(String groupName, DBCollection personCollection){
 // Construct JSON with DBObjects
 DBObject fields = new BasicDBObject("group", groupName);
 // Show what we are doing...
 System.out.println(fields);
 // Sort on name (descent order)
 DBObject orderBy = new BasicDBObject("name", -1);
 // return persons which met the conditions
 return personCollection.find(fields).sort(orderBy);
 }
public static void main(String[] args) throws UnknownHostException{
 // Application is aware of replicaset of mongoInstances
 List addr = new ArrayList();
 addr.add(new ServerAddress("127.0.0.1", 40001));
 addr.add(new ServerAddress("127.0.0.1", 40002));
 addr.add(new ServerAddress("127.0.0.1", 40003));
 // Connect to replica set
 Mongo mongo = new Mongo(addr);
 // Get the Person Collections from test database
 DBCollection collection = mongo.getDB("test").getCollection("person");
 // Just for the fun count number of documents in person collection
 System.out.println(" number of documents : " + collection.getCount());
 PersonReport personReport = new PersonReport();
 DBCursor cursor = personReport.getPersonWithGroup("Big Data", collection);
 // Loop through found persons...
 try {
 while(cursor.hasNext()) {
 System.out.println(cursor.next());
 }
 } finally {
 cursor.close();
 }
}
}

 

Na het starten krijg je de volgende output:

 


number of documents : 1000001
{ "group" : "Big Data"}
{ "_id" : { "$oid" : "50904c1912644ddba9ec6c1e"} , "name" : "Maikel" , "group" : [ "Oracle" , "ExaData" , "Big Data"]}

                                          

 

Nu is het tijd om de primary instance te stoppen. Check wat er gebeurt met de secondary (rs.status()). De arbiter kiest een nieuwe primary instance. Start het Java programma opnieuw. Als het goed is krijg je gewoon dezelfde output, zonder het Java programma aan te passen!                               

Als de oude primary weer in de lucht komt, start het recovery proces. De oude primary fungeert nu als nieuwe secondary. Ook nu zal de applicatie gewoon blijven werken!

 

MongoDB v2.4 features

Op het moment van schrijven heeft v2.4 de productie status gekregen. Hieronder de belangrijkste features:

Text search is een van de meest gevraagde functies voor MongoDB en 10gen werkt al een tijdje aan een experimentele tekst-zoekfunctie. Advies is wel om text search niet direct op de productieomgevingen te implementeren, maar eerst te proberen op testomgevingen.

Google’s V8 zal Mozilla’s SpiderMonkey vervangen als de onderliggende JavaScript-engine. Hoewel er wellicht enkele voordelen behaald kunnen worden, kan er geen sprake zijn van opmerkelijke verschillen voor de meeste MongoDB gebruikers. Er zijn wel benchmarks uitgevoerd,  waaruit blijkt dat het effect het grootste is in MongoDB omgevingen waar sharding (partitioning) is ingezet.

Verder zijn er nog enkele nieuwe (Geospatial) indexen bijgekomen en is hash-based sharding geïntroduceerd, om nog betrouwbaarder de data te distribueren over de verschillende shards. Daarnaast is er nu een modulair opgezet authenticatie mechanisme met support voor Kerberos.

 

 

10gen is met MongoDB community gedreven, iedereen kan suggesties (tickets) inbrengen en volgen op: https://jira.mongodb.org/

Voordelen MongoDB

  • MongoDB kan als generieke database worden ingezet en is flexibel in te richten
  • MongoDB kan overweg met grote hoeveelheden data:
  • hoog beschikbaar onder andere door middel van replicatie
  • horizontale schaalbaarheid onder andere door middel van sharding
  • Flexibel modelleren (geen joins, maar nested values)
  • Gemakkelijk in gebruik

Referenties

MongoDB http://www.mongodb.org/

10gen http://www.10gen.com/