Follow Us on Twitter

MongoDB, het einde van RDBMS?

Februari 2017 - De bekendste soort database is al met ons sinds de 60’er jaren: de relationele database (RDMS). Wie is er niet opgegroeid met datamodellering, tabellen normaliseren, indexen en foreign key constructies? In de 21e eeuw is de RDMS nog steeds een goede manier om gestructureerde data op te slaan. Echter, sinds het internet- en het BigData-tijdperk zijn er twijfels ontstaan of de relationele database wel the way to go is voor het opslaan van heel veel ongestructureerde data. De NoSQL beweging zag het licht, en geeft een alternatief voor de relationele database.

Dit Whitebook zal ik de documentdatabase introduceren en de grootste verschillen bespreken. In het laatste hoofdstuk zal ik een eenvoudig voorbeeld bespreken aan de hand van het (bekende) HR-schema uit de Oracle database.

NoSQL?

Zoals mijn collega Patrick Sinke al schreef in zijn Whitebook uit 2014, is NoSQL (Not Only SQL) “de verzamelnaam voor een brede groep databasemanagementsystemen die zich voornamelijk onderscheidt van relationele databases in het ontbreken van tabelrelaties ten gunste van eenvoud, schaalbaarheid en beschikbaarheid.”.

NoSQL komt in een vijftal smaken:

  • Key-value stores
    In deze database wordt data opgeslagen in key-value paren. Oracle’s NoSQL is een voorbeeld van een key-value database.
  • Wide-column stores
    Wide-column database slaat de data niet op in rijen, maar in kolommen. Het is eigenlijk een speciale vorm van key-value databases. Een kolom is een key-value paar bestaande uit drie elementen: De kolomnaam, de waarde, en een tijdstempel.
  • Graph databases
    Deze vorm van een NoSQL database is special geschikt voor het opslaan van data dat topologiën en nodes, graven e.d. representeert.
  • Document databases
    Hier gaat dit Whitebook over. Een document database is een database die documenten opslaat. Documenten zijn datastructuren bestaande uit (meerdere) key-value paren, geneste structuren, array’s etc. Een populaire implementatie van een documenten database is MongoDB.

MongoDB?

Dit Whitebook zoomt verder in op de documentdatabases, en dan met name MongoDB. MongoDB is een open source documentdatabase.

Wat is een Document

Maar wat is nu precies een document? En hoe zit dat in een database? Kort door de bocht gezegd is een document in MongoDB een JSON-document. JSON (JavaScript Object Notation) is een human readable groep, of set, van geordende key-value pairs. Je kan deze JSON-documenten vergelijken met een rij in een tabel in een RDMS.

Een voorbeeld van een JSON-document “whitebook” is:

{
	"Auteur": {
		"Voornaam": "Laurens",
		"Achternaam": "van der Starre"
	},
	"Titel": "MongoDB",
	"Publicatiemaand": "maart",
	"Publicatiejaar": 2017
}

Belangrijk om te melden is dat er geen vooraf opgestelde structuur van een document in MongoDB nodig is. De documenten in MongoDB zijn “schema-less” en ongestructureerd.

Document versus relationeel

Als je uit de relationele database wereld komt, is het misschien lastig om in het begin direct de overstap te maken naar de documentdatabase wereld. Daarom leg ik eerst even de begrippen naast elkaar:

RDMS MongoDB
Database of schema       Database
Tabel Collection
Rij Document
Kolom Field
Primary key Primary key
Index Index
View Virtual

Zoals je direct kan zien zijn de overeenkomsten groot. Eigenlijk is het grootste verschil tussen een RDMS en MongoDB dat je in de relationele wereld praat over een tabel met rijen en kolommen, je in de MongoDB-wereld het hebt over een collectie van documenten die opgebouwd is uit velden.

Structuur

Een heel groot verschil tussen RDMS en MongoDB is dat de documenten ongestructureerd zijn. Zo kan het zijn dat je in een bepaalde Collection documenten hebt opgeslagen die compleet verschillend zijn van structuur. Iets wat in een relationele database ondenkbaar is. Immers, in een relationele database zijn de tabellen strak gedefinieerd.

In een “employees”-collection kunnen dus prima de volgende documenten opgeslagen zijn, die anders zijn qua structuur:

{
	"_id": 1234,
	"fist_name": "Laurens",
	"last_name": "van der Starre",
	"email": "laurens.van.der.starre@whitehorses.nl",
	"phone_number": "06-12345678",
	"hire_date": "2011-05-01T00:00:00",
	"job_id": 4567,
	"salary": 123456,
	"commission_pct": 15,
	"manager_id": 1,
	"department_id": 1
}

en

{
	"_id": 45678,
	"voornaam": "Laurens",
	"achternaam": "van der Starre",
	"email": "laurens.van.der.starre@whitehorses.nl",
	"job_id": 4567,
	"salary": 9875564,
	"commission_pct": 15,
	"manager_id": 1,
	"department_id": 1
}

Het gemis van de structuur betekent dat MongoDB veel vrijheden geeft aan de databasegebruiker. Dit betekent dus ook automatisch dat de databasegebruiker (zoals een applicatie) zelf verantwoordelijk is om er voor de zorgen dat het niet een grote chaos wordt in de Collection. In een RDMS wordt automatisch de datastructuur en integriteit bewaakt. Aan de andere kant stelt MongoDB je in staat heel snel data (in de vorm van documenten) te kunnen opslaan, waarbij je door slim te programmeren rekening kan houden met hiaten en missende data in de ongestructureerde data. Het is dus heel erg flexibel, maar legt veel verantwoordelijkheid bij de databasegebruiker.

Uiteraard bestaan er bewegingen om structuur te brengen en het ongestructureerde aspect van de documenten uit te bannen. Mongoose is hier een voorbeeld van: het kan de structuur van documenten afdwingen, vergelijkbaar hoe een relationele database de structuur van de tabellen afdwingt.

Relaties

Het woord “relationeel” in RDMS geeft al aan dat de data allerlei relaties met elkaar kan hebben. In een RDMS wordt de data genormaliseerd over diverse tabellen die aan elkaar gekoppeld worden met foreign key constructies. Dit is iets wat MongoDB niet heeft. In de documentdatabase bestaat de foreign key zoals we die kennen simpelweg niet. Het joinen van data zoals we kennen uit SQL-queries is iets wat volledig geprogrammeerd dient te worden in MongoDB. De “foreign keys” in documenten zijn dus niets anders dan fields met een waarde die toevallig hetzelfde zijn als een waarde van een field in een ander document. Om deze data-integriteit te behouden moet de databasegebruiker dit allemaal zelf regelen. Het wordt niet automatisch afgedwongen. Het “joinen” van data in een documentdatabase is dus duur t.o.v. een relationele database.

Indexen

Uiteraard mag van MongoDB, net als bij elke database, verwacht worden dat data efficiënt kan worden bevraagd. Net als in een RDMS kan in MongoDB indexen worden gelegd op velden. Een groot verschil is dat er in MongoDB ook indexen kunnen worden gelegd op sets van velden (compount indexen), speciale indexen op tekstvelden voor het eenvoudig tekst gebaseerd zoeken, en zgn. time-to-live indexen op datumvelden die data automatisch kan verwijderen.

In essentie werken deze indexen vergelijkbaar met de indexen in RDMS, Het gaat iets te ver om in dit Whitebook erg diep in te gaan op deze indexen. Voor de geïnteresseerden kan ik het boek "MongoDB Data Modelling" van Wilson da Rocha Franca aanbevelen.

Queriën

Een database zou een database niet zijn als er niet een query-taal bestaat om de data benaderen. Het bevragen van documenten in MongoDB gaat op een significant andere manier dan in een RDMS. Het zal geen verrassing zijn dat de afwezigheid van de relaties en tabellen niet compatible is met de SQL-taal. Er bestaan diverse libraries om MongoDB te ontsluiten in verschillende programmeertalen zoals bijvoorbeeld C, C++, Java, Python etc. Als je MongoDB benadert met de standaard client, is JavaScript de taal waarin de queries moeten worden geschreven. In dit hoofdstuk zal ik enkele punten aantippen die in MongoDB echt anders zijn dan in een relationele database.

Leesoperatie

De simpelste operatie is het ophalen van een document met de find-operatie.

db.collection.find(
{criteria},
{projection}
)

In het bovenstaande voorbeeld zien we dat we een methode find kunnen toepassen op een Collection collection. De zoek cirteria (de “WHERE-clausule”) zorgt voor de selectie van het gezochte document. De projection geeft aan hoe de data gerepresenteerd is. Hier kan je de velden opgeven die uit de query moeten worden teruggeven, en hoe ze moeten worden gesorteerd.

Maar hoe moeten we de zoekcriteria precies opstellen? In de relationele wereld is dit eenvoudig: de tabelstructuur staat vast dus het is een kwestie van de juiste WHERE-clausule opstellen. Echter hebben we eerder al gezien dat de structuur van de documenten in een collection kunnen verschillen. De zoekcriteria kan dus voor een groot deel van de documenten helemaal niet van toepassing zijn. Het “employees”-voorbeeld uit het eerdere hoofdstuk bevat twee werknemer-documenten, beide van een andere structuur. De query om de werknemer te vinden zonder ingevuld telefoonnummer:

db.employees.find(
 {phone_number: ""}
 )

zal daarom geen resultaten retourneren, immers, het hele veld phone_number hoeft niet te bestaan, dus zoeken op een leeg veld hoeft niet alle resultaten te bevatten. MongoDB heeft speciale operaties om daar mee om te gaan. Zo bestaat de $exists operator die bepaalt of een veld bestaat in het document (niet te verwarren met checken op null). De query:

db.employees.find(
{$or:[
     {phone_number:""},
     {phone_number:{$exists:false}}
     ]
}
)

vindt wel het gewenste resultaat.

Aggregeren en groeperen
Iedereen zal beamen dat je met een simpele SELECT-query in een relationele database vaak niet direct de gewenste resultaten verkrijgt. Vaak wil je de data groeperen en ordenen om tot het gewenste resultaat te komen. In MongoDB kan dit met de aggregate functie:

db.mycollection.aggregate([opr 1, opr 2, ... opr n])

Deze functie is een pipeline van verschillende operaties sequentieel uitgevoerd op de collection. De pipeline is te vergelijken met de werking van het pipe-symbool | in Linux. Elke operatie in de pipeline werkt op het resultaat van de vorige stap (met de eerste stap werkend op de hele collectie). Er bestaan operaties voor het simpelweg aangeven welke velden uit de documenten moeten worden doorgegeven, limiteren hoeveel, filteren op basis van criteria, groeperen etc.

Joinen
Zoals al eerder genoemd, is het joinen van data minder eenvoudig dan met een relationele database. Sinds MongoDB 3.2 is het maken van een Left Outer Join tussen twee collections wel iets eenvoudiger geworden doordat de $lookup operatie is geïntroduceerd in de aggregate functie.

MongoDB - De $lookup operatie
Figuur 1 - De $lookup operatie

De $lookup operatie matcht een veld in de pipeline (zie Figuur 1) “leftVal” met een veld in een andere collection (“rightVal”) en neemt deze data op in de pipeline in een nieuw data array “embeddedData”. Door een andere aggregate-operatie $unwind te gebruiken, kan deze array uit elkaar getrokken worden om zo echt een left outer join te krijgen. Deze operatie stelt je dus in staat een soort van join-operatie uit te voeren, echter nog steeds niet zo eenvoudig als in een traditionele SQL-database.

Schrijfoperatie
Het spreekt voor zich dat MongoDB insert en update operaties heeft. Een leuke feature is dat je bij deze operaties een zgn. “WriteConcern” mee kan geven. Normaal gesproken is een schrijfactie non-blocking, wat inhoudt dat MongoDB op de achtergrond de insert of update persisteert in de database. De gebruiker hoeft hier niet op te wachten. Een WriteConcern kan bij de insert of update operatie worden meegegeven, en dat geeft de gebruiker de mogelijkheid te wachten totdat MongoDB aangeeft dat er is voldaan aan de WriteConcern, en vervolgens pas de wachtsituatie op te geven (acknowlegded mode). Een WriteConcern kan volledig door de gebruiker worden opgesteld, en zou bijvoorbeeld dusdanig kunnen worden opgesteld dat de insert of update moet wachten totdat de actie daadwerkelijk op disk is gepersisteerd, en wellicht ook op cluster nodes (replica acknowledged) (zie Figuur 2, ontleend uit MongoDB Data Modelling van Wilson da Rocha Franca).

Op het moment dat MongoDB geclusterd en high-available is uitgevoerd kan je schrijfacties laten garanderen dat de desbetreffende actie ook daadwerkelijk is op meerdere nodes. De WriteConcern kan worden geconfigureerd op basis van specifieke eigenschappen van een node (zgn. tags). Tags kunnen bijvoorbeeld aangeven waar de node staat in het datacentrum, of dat het SSD-schijven heeft. Zo kan er bijvoorbeeld een WriteConcern worden opgesteld dat garandeert dat een schrijfactie minimaal op een node met SSD-schijven is gepersisteerd.

Figuur 2 Writeconcern. Wachten totdat er gegarandeerd is gepersisteerd (replica acknowledged)
Figuur 2 Writeconcern. Wachten totdat er gegarandeerd is gepersisteerd (replica acknowledged)

Datamodellering

Het ontwerpen van een datamodel van een applicatie die gebruikmaakt van een document database is compleet anders dan een datamodel binnen een relationele database.

Bij een traditioneel relationeel datamodel ontwerp wordt er vaak aan de hand van een logisch (data)model een set van genormaliseerde tabellen gedefinieerd met allerlei onderlinge relaties. De applicatieontwikkelaar kan vervolgens allerlei SQL-queries --en wellicht VIEWS—gebruiken om de data te ontsluiten in de applicatie. Echter bij een documentdatabase gaat deze vlieger niet op. Het joinen van data, en het in stand houden van al de onderliggende relaties tussen de verschillende collections is erg duur en minder efficiënt. Bij een documentdatamodel zal het datamodel veel dichter bij de behoefte van de applicatie liggen: als de applicatie altijd de behoefte heeft een bepaalde set aan data op te halen, die logisch als twee losse entiteiten zijn gedefinieerd, waarom zou je deze dan niet samenvoegen tot, of repliceren in, een enkel document?

Voorbeeld

In een Oracle-database wordt standaard het HR-schema geleverd. Dit schema bevat een voorbeeld datamodel waarin werknemers (employees) kunnen worden opgeslagen in relatie met o.a. hun afdeling (department), hun job en -historie. Dit is een relatief simpel schema en is hieronder weergegeven.

MongoDB - HR-schema
Figuur 3 HR-schema

Zoals de figuur al weergeeft bevat het HR-schema een reeks aan relaties en foreign keys. Een serie aan slimme SQL-queries stelt je in staat alle benodigde gerelateerde data uit de tabellen te halen.

Als we dit model in een documentdatabase zoals MongoDB willen onderbrengen, dan kunnen we natuurlijk eenvoudig een 1-op-1 conversie doen van dit model naar collections met documents. We kunnen een collection Employee maken, en daar bijvoorbeeld een employee document onderbrengen:

{
	"_id": 1234,
	"fist_name": "Laurens",
	"last_name": "van der Starre",
	"email": "laurens.van.der.starre@whitehorses.nl",
	"phone_number": "06-12345678",
	"hire_date": "2011-05-01T00:00:00",
	"job_id": 4567,
	"salary": 123456,
	"commission_pct": 15,
	"manager_id": 1,
	"department_id": 1
}

Om het department op te slaan kunnen we een collection departments maken met daarin bijvoorbeeld een document:

{
	"department_id": 1,
	"department_name": "Consulting",
	"manager_id": 1,
	"location_id": 42
}

Analoog voor bijvoorbeeld Country:

{ 
	"country_id": 31,
	"country_name": "Nederland",
	"region_id": 56
}

Maar wat nou als we altijd ook de afdeling en locatie nodig hebben i.c.m. de medewerker? Iedere keer dat de data opgehaald moet worden moet je dus een aggregate met $loopup-operaties uitprogrammeren. In deze situatie is dat niet efficiënt. Een tussenoplossing is om de afdelings- en locatiedata op te slaan bij het employee document:

{
	"_id": 1234,
	"fist_name": "Laurens",
	"last_name": "van der Starre",
	"email": "laurens.van.der.starre@whitehorses.nl",
	"phone_number": "06-12345678",
	"hire_date": "2011-05-01T00:00:00",
	"job_id": 4567,
	"salary": 123456,
	"commission_pct": 15,
	"manager_id": 1,
	"Department": {
		"Department_name": "Consulting",
		"Location": "Nederland",
		"department_id": 1
	}
}

Er is dus enige dataduplicatie, maar het ophalen van het document gaat efficiënter dan voorheen. Het wijzigen van locatiedata vereist nu meer effort, dus het is belangrijk om a.d.h.v. de requirements hier een gulden middenweg in de vinden.

Het bovenstaande wil dat de gebruiker volledig verantwoordelijk is voor het updaten van alle collections als er op een gegeven moment een afdeling van naam verandert bijvoorbeeld. Iets wat in de relationele variant van dit schema niet aan de orde zou zijn.

Conclusie: het einde van RDMS?

In dit Whitebook heb ik in een vogelvlucht MongoDB de revue laten passeren. Natuurlijk is dit nog lang niet alles uit de wereld van de documentdatabase. Ik heb een paar grote verschillen aangestipt tussen een relationele database en een documentdatabase. Eigenlijk is het grootste verschil dat je ongestructureerde documenten in een collection hebt i.t.t. genormaliseerde tabellen met onderlinge relaties. Dit grote verschil brengt aan de ene kant grote vrijheden met zich mee, en aan de andere kant vereist het een compleet andere insteek qua datamodellering. De databasegebruiker moet veel meer zelf doen om de data consistent te houden, en het datamodel ligt veel dichter bij de applicatie dan bij een relationeel datamodel.

Maar is dit het nieuwe database walhalla? Ik denk van niet. Beide databasesoorten vullen hun eigen niche. Grote systemen, met veel datadomeinen, waarop rapportages en datawarehouse operaties op plaatsvinden kunnen beter in een relationele database blijven. Natuurlijk kan dit laatste ook in MongoDB, maar het gaat simpelweg minder makkelijk. Ook moet afgevraagd worden of het hele vrije en ongestructureerde aspect van de documenten in MongoDB uiteindelijk de onderhoudbaarheid ten goede komt. Een gat dat Mongoose dan weer kan opvullen.

Is MongoDB het einde van RDMS? Ik denk het dus niet. Het ligt maar net aan de toepassing of een documentdatabase past of niet. Zo zijn er veel gebieden waar ik denk dat een RDMS nog steeds heer en meester is. Per toepassing zou moeten worden gekeken of een documentdatabase de oplossing biedt, of dat een RDMS betere invulling geeft. Je ziet in de praktijk dat een document based database zoals MongoDB meestal direct onder de applicatie is gepositioneerd. Omdat de applicatie de structuur bepaalt van de data dat opgeslagen wordt hoeven er geen data manipulaties of conversie scripts gedraaid te worden om de data en tabellen weer zo te vormen dat het weer aansluit op de behoefte van de applicatie. En dat laatste is eigenlijk het grootste verschil.

Referenties

  1. Wat is Oracle NoSQL, Patrick Sinke Whitebook 2014
    http://www.whitehorses.nl/whitebooks/2014/wat-oracle-nosql-en-hoe-gebrui...
  2. Instant MongoDB, Amol Nayak
    Packt Publishing, 2013, ISBN 978-1-78216-970-3
  3. MongoDB Data Modelling, Wilson da Rocha Franca
    Packt Publishing 2015, ISBN 978-1-78217-534-6
  4. Column data store Wikipedia.
    https://en.wikipedia.org/wiki/Column_(data_store)
  5. Joins and Other Aggregation Enhancements.
    https://www.mongodb.com/blog/post/joins-and-other-aggregation-enhancemen...
Waardering:
 
Tags:

Reacties

Nieuwe reactie inzenden

De inhoud van dit veld is privé en zal niet openbaar worden gemaakt.

Meer informatie over formaatmogelijkheden

CAPTCHA
Deze vraag is om te testen of u een persoon bent en om spam te voorkomen
Image CAPTCHA
Enter the characters shown in the image.