Gigantische Datenbank

Werbung
NoSQL-Datenbank MongoDB Datenbanken
Gigantische Datenbank
Die humongous database oder kurz MongoDB hat einen einprägsamen Namen
und ist eine vielversprechende NoSQL-Datenbank. MongoDB möchte die Lücke
zwischen Key-Value-Stores (die schnell und hochskalierbar sind) und traditionellen relationalen Datenbanken (die mächtige Queries und viel Funktionalität
bieten) schließen. Im folgenden Artikel wird diese skalierbare, hochperformante
und dokumentenorientierte Datenbank vorgestellt.
von Maximilian Weber
ongoDB ist eine dokumentenorientierte Datenbank, die mit
JSON-ähnlichen Dokumenten
arbeitet. Auf Grund des nichtrelationalen Datenmodells wird MongoDB auch
als NoSQL-Datenbank bezeichnet. Neben dokumentenorientierten Datenbanken gelten als Hauptkategorien für
NoSQL-Datenbanken auch Key-ValueStores, BigTable-Klone und Graphendatenbanken. Beim Vergleich der verschiedenen Ansätze sind zwei Punkte
entscheidend: die Größe der zu erwartenden Datenmenge und die Komplexität des Datenmodells [1]. Dabei gilt, je
einfacher das Datenmodell, desto eher
ist eine Datenbank in der Lage, bei sehr
großen Datenmengen zu skalieren. KeyValue-Stores und BigTable-Implementierungen haben ein simples Datenmodell und können dadurch Daten relativ
einfach horizontal partitionieren, skalieren also sehr gut bei der Datenmenge.
Dokumenten- und Graphendatenbanken (siehe S. 74) haben sich hingegen für
ein reicheres Datenmodell entschieden.
Artikelserie
Teil 1: Was ist MongoDB?
Teil 2: Java-MongoDB-Treiber
www.JAXenter.de
Die Skalierbarkeit für die Datenmenge
nimmt dadurch leicht ab, während die
Skalierbarkeit für die Komplexität der
Daten zunimmt. Komplexere Objektmodelle können auf die beiden letzteren
Datenmodelle also besser abgebildet
werden.
Daten werden bei MongoDB in
JSON-ähnlichen Dokumenten abgelegt.
Listing 1 zeigt exemplarisch ein solches
JSON-Dokument, das einen Blogeintrag repräsentieren soll. Im Allgemeinen
kann JSON als Datenaustauschformat
komplexe Datenstrukturen abbilden
und kennt Objekte, Arrays, Zeichenketten, Zahlen, boolesche Werte und null.
MongoDB arbeitet mit dem JSON-ähnlichen BSON-Format [2], was die Kurzform für Binary JSON ist. BSON ist eine
binärkodierte Serialisierung von JSONähnlichen Dokumenten und kennt weitere Datentypen, wie z. B. Datumswerte.
Es wird sowohl für die Netzwerkkommunikation als auch für die Speicherung
der Dokumente auf dem Dateisystem
verwendet.
Im Folgenden werden die JSONBestandteile wie z. B. in Listing 1 title als
Schlüssel und Gigantische Datenbank
als Wert bezeichnet, diese wiederum
bilden zusammen eine Eigenschaft.
MongoDB kann Dokumente wie in Lis-
ting 1 in Form von BSON-Dokumenten
speichern. Das Datum des Blogeintrags könnte dabei weiterhin ein String
bleiben, oder man könnte hierfür den
MongoDB-Datentyp Date verwenden.
Der Datentyp Date bietet den Vorteil,
dass dieser im Gegensatz zum String
innerhalb eines MongoDB-Queries
mit einem anderem Datumswert verglichen werden kann. Solche dynamischen
(ad hoc) Queries sind eine der großen
Stärken der MongoDB-Datenbank.
Prinzipiell kann innerhalb eines Queries [3] nach jeder Eigenschaft eines
BSON-Dokumentes gefiltert werden.
Für das Beispiel in Listing 1 wäre es also
ohne Weiteres möglich, aus einer MongoDB alle Blogeinträge herauszufiltern,
Listing 1
{
"title": "Gigantische Datenbank",
"tags" : ["MongoDB","NoSQL"],
"date" : "Sun Feb 01 2009 01:00:00 GMT+0100 (CET)",
"body" : "MongoDB ist eine dokumentenorientierte ..."
"comments":[
{"author":"Max Mustermann", "comment":"MongoDB rocks!”}
]
}
javamagazin 7|2010 79
Datenbanken NoSQL-Datenbank MongoDB
die mit „NoSQL“ getaggt und von Max
Mustermann kommentiert worden
sind. Das Anlegen eines Index [4] für die
entsprechenden Eigenschaften ist dafür
nicht notwendig, würde allerdings die
Geschwindigkeit der Datenbankoperation steigern und empfiehlt sich daher
bei häufig verwendeten Eigenschaften.
MongoDB besitzt viele weitere mächtige Datenbankoperationen, die in ihrer
Gesamtheit dem Funktionsumfang von
SQL sehr nahe kommen. Neben dynamischen Queries unterstützt MongoDB
auch Map-Reduce-Operationen.
Elemente einer MongoDB
Eine MongoDB kann eine oder mehrere Datenbanken enthalten, die nicht in Tabellen,
sondern in Collections
unterteilt. Eine Collection enthält wiederum
die (BSON-)Dokumente.
Die Dokumente innerhalb einer Collection sind
schemafrei, d. h. sie müssen keinem bestimmten Muster folgen, wie das beim
relationalen Datenbankschema der Fall
ist. Wären Blogeinträge wie aus Listing 1
in einer Collection blogentries gespeichert, dann könnten sich in dieser Collection auch noch andere Dokumente
mit beliebigem Dateninhalt befinden.
In der Praxis macht das natürlich keinen
Sinn, da sich ansonsten Queries und die
Definition von Indizes schwieriger gestalten würden. Die Tatsache, dass eine
Collection diese Fähigkeit hat, ermöglicht jedoch die Weiterentwicklung des
„dynamischen Schemas“ eines Blogeintrags, ohne bestehende Dokumente in
der Collection ändern zu müssen. Im
Beispiel in Listing 1 könnte die TagFunktion erst später hinzugefügt worden sein, so muss nicht jedes Dokument
eine Eigenschaft tags besitzen.
mit einer kurzen Beschreibung enthalten sind. Das Zeichen „>“ zeigt an, dass
in dieser Zeile ein Befehl auf der Konsole
eingegeben wurde.
Mit der ersten Zeile wird MongoDB
mitgeteilt, dass die Datenbank webshop
verwendet werden soll. Datenbanken
und Collections müssen bei MongoDB
nicht explizit angelegt werden, weil sie
bei der ersten Verwendung automatisch
erzeugt werden. In Zeile 2 wird ein Dokument mit Kundendaten in die Collection
customer gespeichert. Das Dokument
wird dabei in einer JSON-Syntax eingegeben. In Zeile 3 wird mit db.customer.
findOne() genau ein Dokument in der
Collection customer gesucht. Da keine
Filterkriterien angeben wurden und der soeben angelegte
Kunde das einzige Dokument
innerhalb der Collection ist,
wird er nun auf der Konsole
angezeigt (Zeilen 4 bis 10).
Hier finden sich alle zuvor
eingegebenen Daten wieder,
zzgl. einem automatisch hinzugefügten
Schlüssel mit dem Namen _id. Der Wert
des Schlüssels ist die eindeutige Kennung für ein Dokument innerhalb einer
MongoDB-Collection.
In Zeile 11 wird ein weiterer Kunde
angelegt, der in Berlin wohnt. Anschließend wird in der Collection customer
nach allen Dokumenten gesucht, die
die Eigenschaft {city : Berlin} haben.
Erwartungsgemäß wird nur der Kunde
Otto Normal aus Berlin in der Ergebnismenge angezeigt (Zeile 13). Die Abfrage
bezieht nicht nur Kundendatensätze mit
ein, sondern alle Dokumente, die sich in
der Collection customer befinden. Ein
Dokument der Art {id: 1, city : „Berlin“}
wäre also auch Teil der Ergebnismenge.
In Zeile 14 wird ein Index für das Feld id
(die Kundennummer) erzeugt. Die Option unique sorgt dafür, dass es die Eigenschaft mit diesem Wert nur einmal in
der Collection customer geben darf. Die
„1“ gibt an, dass der Index aufsteigend
sein soll (mit „-1“ würde ein absteigend
sortierter Index erzeugt).
Eigenschaften, die sich in einem
eingebetteten Objekt befinden – wie beispielsweise bankCode im Objekt bankData – können über eine Punktnotation
innerhalb von Datenbankoperationen
Neben dynamischen Queries
unterstützt MongoDB auch
Map-Reduce-Operationen.
Listing 2
1 > use webshop
2 > db.customer.save({id: 4711,
name: "Max Mustermann",
city: "Cologne", numberOfOrders: 3});
3 > db.customer.findOne();
4{
5
"_id" : ObjectId("4b9945fe1c23e16a3c0a77c1"),
6
"id" : 4711,
7
"name" : "Max Mustermann",
8
"city" : "Cologne",
9
"numberOfOrders" : 3
10 }
11 > db.customer.save({"id" : 1234, "name": "Otto Normal", city: "Berlin",
numberOfOrders: 4, bankData: {accountNumber : "9876543210",
bankCode : "30020011", accountHolder : "Otto Normal"} });
12 > db.customer.find({city: "Berlin"});
13 { "_id" : ObjectId("4b9b7bc4d9433f42225bfbb6"), "id" : 1234,
"name" : "Otto Normal", "city" : "Berlin", "numberOfOrders" : 4,
"bankData" : { "accountNumber" : "9876543210", "bankCode" :
"30020011", "accountHolder" : "Otto Normal" } }
14 > db.customer.ensureIndex({id : 1}, {unique: true})
15 > db.customer.update({id: 1234}, { $inc : { numberOfOrders : 1} });
16 > db.customer.find({ numberOfOrders : { $gt: 4} });
17{ "_id" : ObjectId("4b9947b01c23e16a3c0a77c2"), "id" : 1234,
"name" : "Otto Normal", "city" : "Berlin", "numberOfOrders" : 5 }
80 javamagazin 7|2010
Quickstart
Eine MongoDB ist schnell aufgesetzt.
Der Quickstart [5] des Projekts macht
seinem Namen alle Ehre. Ohne jegliche
Installation kann aber auch unter http://
try.mongodb.org/ die MongoDB-Konsole direkt im Webbrowser ausprobiert
werden. Bei der MongoDB-Konsole
handelt es sich um eine interaktive JavaScript-Konsole, in der via JavaScriptKommandos mit einer MongoDBInstanz interagiert werden kann. Die
Konsole findet man im Installationsverzeichnis unter bin/mongo, während die
Instanz über bin/mongod gestartet wird.
Ein ausführliches Beispiel
Listing 2 zeigt ein Beispiel, wie man
über die MongoDB-Konsole mit einer
MongoDB-Instanz (mongod-Prozess)
interagieren kann. Als Domäne für das
Beispielszenario wurde ein Webshop gewählt. Für einen Kunden des Webshops
sind jeweils der Name, eine Kundennummer (id), der Wohnort, die Anzahl
der bisherigen Bestellungen und eventuell eine Bankverbindung hinterlegt. Mit
dem help()-Befehl lässt sich jeweils eine
Liste auf der Konsole anzeigen, in der die
zur Verfügung stehenden Kommandos
www.JAXenter.de
NoSQL-Datenbank MongoDB Datenbanken
angesprochen werden. Alle Kunden, die
bei Bankverbindung die BLZ 30020011
angegeben haben, können so z. B. mit
db.customer.find({"bankData.bankCode" : "30020011"}) gefunden werden.
Die Zeile 15 zeigt ein so
genanntes Inplace-Update.
Diese Art von Operationen
ist sehr effizient, da nur die
angegebene Eigenschaft
manipuliert wird und dafür
nicht das gesamte Dokument geladen werden muss.
Im Beispiel wird eine Inkrement-Operation für den Wert der Eigenschaft numberOfOrders des Kunden mit der id 1234
(Otto Normal) durchgeführt. Neben
der Inkrement-Operation unterstützt
MongoDB viele weitere atomare Operationen [6]. Die in Zeile 16 zu sehende
Abfrage sucht alle Kunden, die mehr als
vier Bestellungen getätigt haben. Durch
die zuvor getätigte Inkrement-Operation ist der Kunde Otto Normal nun in der
Ergebnismenge der Abfrage enthalten.
Das Beispiel in Listing 2 enthält keinerlei Befehle für irgendeine Form der
Transaktionssteuerung. Im Gegensatz
zu relationalen Datenbanken existieren bei dem überwiegenden Teil der
einer MongoDB-Datenbank. Mittels einer DBRef kann direkt zu einem
verknüpften Dokument gesprungen
werden, ohne dafür einen Query nach
der ID des verknüpften Dokuments
ausführen zu müssen. Im
Webshop-Beispiel könnten
so die Bankverbindungsdaten auch in einer eigenen
Collection gespeichert sein
und nur aus dem jeweiligen
Kundendatensatz referenziert werden.
Ein Punkt bei der Wahl einer
NoSQL-Datenbank ist auch die
horizontale Skalierbarkeit.
NoSQL-Datenbanken (auch bei MongoDB) keine Transaktionen, Join-Anweisungen oder referenzielle Integrität
(Fremdschlüssel). Datenbankoperationen sind bei MongoDB auf der Ebene
eines einzelnen Dokuments atomar.
Vergleichbar mit einem Fremdschlüssel (ohne referenzielle Integrität) bietet
MongoDB die so genannten Database
References [7] an. Eine DBRef ist eine
Eigenschaft innerhalb eines Dokuments
und verweist auf ein anderes Dokument
Horizontale Skalierung
In den vorangegangen Abschnitten
wurden in erster Linie die Vorteile des
nichtrelationalen Datenmodells einer
MongoDB beleuchtet. Ein weiterer ausschlaggebender Punkt bei der Wahl einer NoSQL-Datenbank ist jedoch auch
die horizontale Skalierbarkeit. Idealerweise soll ein Cluster eines NoSQLSystems dabei dynamisch um weitere
Rechner wachsen können und für sehr
1/2 EM
www.JAXenter.de
javamagazin 7|2010 81
Datenbanken NoSQL-Datenbank MongoDB
Solche Systeme sind dann in der Regel
„Eventually Consistent“ [10].
Auto Sharding
Abb. 1: MongoDB Auto-Sharding
große Datenmengen skalieren. Ein solches System soll möglichst folgende Eigenschaften erfüllen:
■■ Consistency – Alle Knoten des Clusters sehen zum selben Zeitpunkt die
gleichen Daten, auch bei Datenänderungen.
■■ Availability – Beim Absturz von Knoten sollten die verbleibenden Knoten
trotzdem weiterarbeiten können, dabei soll immer wenigstens eine Kopie
der Daten abrufbar sein.
■■ Partition Tolerance – Das Gesamtsystem sollte seine Eigenschaften behalten, auch wenn es auf vielen
Rechnern verteilt läuft.
Zwecks horizontaler Skalierung bietet
MongoDB das so genannte Auto Sharding an. Abbildung 1 zeigt schematisch,
welche Spieler es in einem solchen Szenario gibt.
Ein Shard besteht aus einem oder
mehreren Servern und speichert Daten mithilfe der mongod-Prozesse
(mongod ist der Hauptprozess einer
MongoDB). Typischerweise werden
pro Shard mehrere Server eingesetzt,
um durch Replikation [11] eine höhere Verfügbarkeit zu erreichen. Shards
speichern jeweils nur eine gewisse Anzahl von Chunks. Ein Chunk stellt einen
zusammenhängenden Bereich von Daten (Dokumenten) dar, die zu einer bestimmten Collection gehören. Erreicht
ein Chunk eine Maximalgröße, wird
er in zwei neue Chunks aufgeteilt, und
diese werden eventuell auf einen anderen Shard verlagert. Die Daten werden
im Cluster auf Basis eines so genannten
Shard Key verteilt/partitioniert. Ein
Shard Key wird für eine Collection de-
Szenario mit einem mongos-Prozess
und nicht, wie üblicherweise, mit dem
mongod-Prozess. Ein mongos-Prozess
übernimmt die Koordinationsaufgaben zwischen den einzelnen Komponenten des Clusters und nutzt die
Config-Server, sodass es für den Client
aussieht, als würde er mit einem einzelnen System kommunizieren. An dieser Stelle ist ein kleiner Einblick in die
Fähigkeiten von MongoDB sinnvoll,
was Replikation und Auto Sharding
angeht. Für die weitere Lektüre sei auf
das MongoDB-Wiki [12] verwiesen.
Auto Sharding ist zurzeit nur als Version alpha 3 freigegeben und hat kleinere
Einschränkungen.
Binärdaten
GridFS [13] ist eine weitere Besonderheit von MongoDB. Mithilfe von GridFS
ist es möglich, Dateien in eine MongoDB
zu speichern. GridFS benutzt dabei eine
Collection mit dem Namen files, um die
Metadaten der Dateien zu speichern
(Dateiname, MIME-Type usw.). Die
eigentlichen Daten der Datei kommen
in die Collection chunks. Wie der Name
bereits vermuten lässt, werden die Dateien hier in kleine Stücke
bzw. Chunks aufgeteilt, da
die Größe eines BSONDokuments auf 4 MB limitiert ist. Dies ist aber kein
Nachteil, da es die Chunks
möglich machen, auch
sehr große Dateien gut zu
verwalten. Range-Operationen erlauben es zudem, beispielsweise nur Teile
einer Datei zu laden. Innerhalb eines
MongoDB-Clusters können die Dateien bzw. Chunks außerdem effizient auf
verschiedene Datenbankserver verteilt
werden.
Auch MongoDB muss sich
der Erkenntnis des CAPTheorems beugen.
Das CAP-Theorem [8] (auch
Brewers-Theorem genannt)
besagt, dass ein verteiltes
System stets nur zwei dieser
Eigenschaften zur selben Zeit
erfüllen kann. Auch MongoDB muss
sich dieser Erkenntnis beugen. Welche
zwei der drei CAP-Eigenschaften erfüllt
werden, hängt vom jeweiligen NoSQLSystem und dessen Konfiguration ab.
Einen guten Überblick gibt die Artikelserie über Distributed Consistency [9]
im Blog von MongoDB. Im Standardmodus bietet MongoDB „Strong Consistency“, d. h. dass atomare Lese- und
Schreiboperationen auf der Ebene eines
einzelnen Dokuments unterstützt werden. Transaktionen und damit Consistency über mehrere Entitäten hinweg
findet man typischerweise bei relationalen Datenbanken. Einige NoSQL-Systeme wie Amazon Dynamo oder CouchDB bieten eine höhere Availability und
müssen dafür die Consistency lockern.
82 javamagazin 7|2010
finiert und ist vergleichbar mit einem
Index, der sich auf mehrere Schlüssel
eines (BSON-)Dokuments bezieht.
Enthält eine Query einen Schlüssel, der
sich im Shard Key befindet, kann MongoDB gezielt die Shards herausfinden,
auf denen sich passende Dokumente
befinden müssen. Andernfalls müssen
alle Shards befragt werden, weshalb
der Shard Key sorgfältig anhand der
häufigsten Queries gewählt werden
sollte. Das Konzept des Shard Key ähnelt einigen Elementen aus dem Google
­BigTable Design.
Auf den Config-Server werden die
Metadaten des Clusters verwaltetet,
z. B. welcher Chunk sich in welchem
Shard befindet. Hierbei verbindet
sich der Client beim Auto-Sharding-
Alternativen
Die bekannteste Alternative zu MongoDB als dokumentenorientierte Datenbank ist die NoSQL-Datenbank
CouchDB. Trotz eines sehr ähnlichen
Datenmodells bestehen doch einige
wesentliche Unterschiede zwischen den
beiden Systemen. Die Tatsache, dass
MongoDB in C++ geschrieben ist und
nicht in Erlang wie CouchDB, ist dabei
noch einer der kleineren Unterschiede.
www.JAXenter.de
NoSQL-Datenbank MongoDB Datenbanken
Ein ausführlicher Vergleich zwischen
MongoDB und CouchDB findet sich im
MongoDB Wiki [14].
Fazit
MongoDB ist eine vielversprechende
NoSQL-Datenbank, die sich bereits in
einigen großen Produktivumgebungen bewiesen hat. In der Blogosphäre
sind einige Erfahrungsberichte über
MongoDB im Produktiveinsatz zu finden. So beispielsweise von einigen der
MongoDB-Referenzkunden [15] wie
SourceForge [16], Businessinsider.com
[17] und BoxedIce [18]. In die neue
MongoDB-Version 1.4 sind viele zusätzliche Erfahrungen aus den zahlreichen Produktiveinsätzen eingeflossen.
Einziger Wermutstropfen ist, dass das
MongoDB-Auto-Sharding nur in einer
frühen Version verfügbar ist. So kann
zurzeit nur eine moderate Anzahl von
Shards in einem MongoDB-Cluster be-
trieben werden. Das Ziel ist allerdings,
Cluster zu betreiben, die bis zu 1 000
Shards haben. Wie dieses Ziel erreicht
wird bzw. ob alle MongoDB-Operationen auch in einem Cluster zur Verfügung stehen werden, wird die Zukunft
zeigen.
Der zweite Teil dieser Artikelserie
wird demonstrieren, wie mithilfe des
Java-MongoDB-Treibers die Web­
shopdaten in eine MongoDB gespeichert werden können. Anschließend
soll erläutert werden, wie dies auch mit
Scala und einer Scala-Erweiterung des
MongoDB-Java-Treibers umgesetzt
werden kann. Als weitere ausführliche
Einführung sei auf den Vortrag [19] von
Dwight Merriman – seines Zeichens
CEO von 10gen, der Firma hinter dem
MongoDB-Projekt – hingewiesen.
1/3 epress
Maximilian Weber ist als Softwareentwickler bei der FlowFact AG in
Köln tätig. In einem agilen Team arbeitet er dort an der Entwicklung des
eCRM, einem CRM-System für die Immobilienbranche. Seine Themenschwerpunkte sind Architektur und testgetriebene Entwicklung.
Links & Literatur
[1] http://blogs.neotechnology.com/emil/2009/11/nosql-scaling-to-size-and-scalingto-complexity.html
[2] http://bsonspec.org/
[3] http://www.mongodb.org/display/DOCS/Advanced+Queries
[4] http://www.mongodb.org/display/DOCS/Indexes
[5] http://www.mongodb.org/display/DOCS/Quickstart
[6] http://www.mongodb.org/display/DOCS/Atomic+Operations
[7] http://www.mongodb.org/display/DOCS/Database+References
[8] http://www.royans.net/arch/brewers-cap-theorem-on-distributed-systems/
[9] http://blog.mongodb.org/post/523516007/on-distributed-consistency-part-6consistency-chart
[10] http://www.allthingsdistributed.com/2008/12/eventually_consistent.html
[11] http://www.mongodb.org/display/DOCS/Replica+Pairs
[12] http://www.mongodb.org/display/DOCS/Sharding+Introduction
[13] http://www.mongodb.org/display/DOCS/GridFS
[14] http://www.mongodb.org/display/DOCS/
Comparing+Mongo+DB+and+Couch+DB
[15] http://www.mongodb.org/display/DOCS/Production+Deployments
[16] http://us.pycon.org/2010/conference/schedule/event/110/
[17] http://www.businessinsider.com/how-we-use-mongodb-2009-11
[18] http://blog.boxedice.com/2010/02/28/notes-from-a-production-mongodbdeployment/
[19] http://leadit.us/hands-on-tech/MongoDB-High-Performance-SQL-Free-Database
www.JAXenter.de
javamagazin 7|2010 83
Herunterladen