Dipl.-Math. Tobias Trelle ist Senior IT Consultant bei der code-centric AG, Solingen. Er ist seit knapp 20 Jahren im IT-Business unterwegs und interessiert sich für Softwarearchitekturen und skalierbare Lösungen. Zu diesen Themen veröffentlicht Tobias Artikel in Fachzeitschriften und hält Vorträge auf Konferenzen und Usergruppen. Außerdem organisiert er die Düsseldorfer MongoDB-Usergruppe.
Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+: www.dpunkt.de/plus |
Der praktische Einstieg
Tobias Trelle
tobias.trelle@codecentric.de
Lektorat: René Schönfeldt
Copy-Editing: Sandra Gottmann, Münster
Herstellung: Birgit Bäuerlein
Umschlaggestaltung: Helmut Kraus, www.exclam.de
Druck und Bindung: M.P. Media-Print Informationstechnologie GmbH, 33100 Paderborn
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN
Buch 978-3-86490-153-9
PDF 978-3-86491-533-8
ePub 978-3-86491-534-5
1. Auflage 2014
Copyright © 2014 dpunkt.verlag GmbH
Wieblinger Weg 17
69123 Heidelberg
Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.
Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.
Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.
5 4 3 2 1 0
Für Vera und Kelian
1 Einleitung
1.1 Big Data
1.2 NoSQL
1.3 Dokumentenorientierte Datenbanken
1.4 Verteilte Systeme und das CAP-Theorem
1.5 ACID vs. BASE
1.6 Zusammenfassung
2 MongoDB in 21 Minuten
2.1 Installation
2.2 Server starten
2.3 Mongo Shell
2.4 Erste Schritte
2.4.1 Collections
2.4.2 Dokumente
2.4.3 CRUD-Operationen
2.5 Daten importieren
2.6 Schemafreiheit
2.7 Zusammenfasssung
3 Grundlegende Konzepte
3.1 Konnektivität
3.2 Datenhaltung
3.3 Datenbanken
3.4 Collections
3.4.1 System Collections
3.4.2 Capped Collections
3.5 Dokumente
3.5.1 BSON
3.5.2 Datentypen
3.5.3 ObjectId
3.5.4 Primärschlüssel
3.5.5 Eingebettete Dokumente
3.5.6 Arrays
3.5.7 Dokumentreferenzen
3.6 Indizes
3.7 Namespaces
3.8 Abstraktionsebenen
3.9 Zusammenfassung
4 Replikation
4.1 Das Oplog
4.2 Master-Slave-Replikation
4.3 Replica Sets
4.3.1 Erste Schritte
4.3.2 Zugriff durch Anwendungen
4.3.3 Konfiguration im Detail
4.3.4 Grenzen der Replikation
4.4 Zusammenfassung
5 Sharding
5.1 Grundlagen
5.2 Der Schlüssel zum Sharding
5.3 Konfiguration
5.4 Fortgeschrittene Themen
5.4.1 Chunk-Verwaltung
5.4.2 Shards hinzufügen
5.4.3 Shards entfernen
5.4.4 Tag-basiertes Sharding
5.4.5 Produktive Sharding-Systeme
5.5 Zusammenfassung
6 Queries
6.1 Grundlegendes
6.2 Cursor
6.2.1 pretty()
6.2.2 limit()
6.2.3 skip()
6.2.4 sort()
6.2.5 batchSize()
6.2.6 objsLeftInBatch()
6.2.7 readPref()
6.2.8 snapshot()
6.2.9 showDiskLoc()
6.2.10 addOption()
6.3 Suchkriterien
6.3.1 Gleichheit
6.3.2 Logische Meta-Operatoren
6.3.3 Vergleichsoperatoren
6.3.4 Eingebettete Dokumente
6.3.5 Arrays
6.3.6 Reguläre Ausdrücke
6.3.7 $where
6.3.8 Schemafreiheit
6.3.9 Sonstiges
6.4 Indizes
6.4.1 Zusammengesetzte Indizes
6.4.2 Eingebettete Dokumente
6.4.3 Arrays
6.4.4 explain() und hint()
6.4.5 Covered Query
6.4.6 TTL-Index
6.4.7 Hashed Index
6.5 Profiling
6.5.1 Performance-Optimierung
6.5.2 Tools
6.6 Geodaten-Suche
6.6.1 Übersicht
6.6.2 Planare Koordinaten
6.6.3 Sphärische Koordinaten
6.7 Volltextsuche
6.7.1 Erstes Beispiel
6.7.2 Volltext-Indizes
6.7.3 Volltextsuche
6.7.4 Kombination mit anderen Suchkriterien
6.7.5 Sprachen und Stoppwortlisten
6.7.6 Negation und Suche nach Phrasen
6.7.7 Gewichtete Suche
6.7.8 Mehrsprachige Collections
6.8 Zusammenfassung
7 Manipulation von Dokumenten
7.1 Insert – Dokumente einfügen
7.1.1 Write Concern – der Konsistenzgrad
7.1.2 Mehrere Dokumente einfügen
7.2 Update – Änderungen an Dokumenten
7.2.1 Vollständige Ersetzung
7.2.2 Partielle Änderungen
7.2.3 Upsert – Anlegen und Ändern
7.2.4 FindAndModify – Suchen und Ändern
7.2.5 Änderungen an mehreren Dokumenten
7.2.6 Optimistisches Sperren
7.3 Save – Einfügen bzw. Ändern
7.4 Remove – Löschen von Dokumenten
7.5 Zusammenfassung
8 Schema-Design
8.1 Einleitung
8.2 Analyse und Modelle
8.2.1 Diagramme
8.2.2 Design by Example
8.3 Beziehungen
8.3.1 1:1-Beziehungen
8.3.2 1:n-Beziehungen
8.3.3 m:n-Beziehungen
8.4 Vererbung
8.5 Weitere Muster
8.5.1 Variable Dokumenteneigenschaften
8.5.2 Temporale Datenhaltung
8.5.3 Schema-Migration
8.6 Zusammenfassung
9 Aggregation von Daten
9.1 Abfragemethoden zur Datenaggregation
9.1.1 count()
9.1.2 distinct()
9.1.3 group()
9.2 Das Aggregation Framework
9.2.1 Grundkonzepte
9.2.2 Wie starte ich eine Aggregation?
9.2.3 Die Pipeline-Operatoren
9.2.4 Expressions
9.3 MapReduce
9.4 Zusammenfassung
10 Weiterführende Themen
10.1 GridFS
10.1.1 Zugriff über Treiber
10.2 REST-Schnittstelle
10.2.1 Server-Status ermitteln
10.2.2 Collections lesen
10.3 Sicherheit
10.3.1 Authentifizierung und Autorisierung
10.3.2 Netzwerk
10.4 Zusammenfassung
11 Softwareentwicklung mit MongoDB
11.1 Programmierbeispiele
11.1.1 Ruby
11.1.2 Java
11.2 Persistenz-Frameworks
11.2.1 Ruby
11.2.2 Java
11.3 Zusammenfassung
Anhang
A Referenz der Kommandozeilenoptionen
A.1 Mongo – die Mongo Shell
A.2 mongod – Datenbank-Server
A.3 mongos – Router fürs Sharding
Index
Sie wollen ein Buch über MongoDB lesen und werden als Erstes mit dem Begriff Big Data konfrontiert. Was hat MongoDB mit Big Data zu tun, werden Sie sich fragen. Was ist Big Data überhaupt genau? Um die Motivation zu verstehen, die zur Entstehung von MongoDB (und anderen nichtrelationalen Datenbanken) führte, will ich Ihnen zunächst eine Begriffsdefinition von Big Data geben.
Die grundlegenden technischen Herausforderungen im Big-Data-Umfeld wurden bereits 2001 von Doug Laney (heute Analyst bei Gartner) formuliert1 und sind heute unter der Abkürzung 3V bekannt. Dabei handelt es sich um die Aspekte:
Bei Volume geht es um die schiere Menge an Daten, die pro Zeiteinheit gespeichert werden muss.
Velocity adressiert die Geschwindigkeit, mit der die Daten persistiert, aber auch verarbeitet werden. Die Verarbeitung kann dabei im Batch oder aber auch in (Fast-)Echtzeit gewünscht sein.
Der Aspekt Variety bezieht sich auf die unterschiedlichen Grade von Strukturiertheit der Daten, das reicht von völlig unstrukturiert über semistrukturiert bis hin zu stark strukturiert.
Daneben machen zwei weitere Dinge den Begriff Big Data aus: Kostenersparnis durch elastische Skalierung und die Gewinnung neuer Informationen aus den Daten.
Ich möchte Ihnen folgende Fragen in Bezug auf Ihr relationales Datenbanksystem stellen. Haben Sie Probleme mit der Anzahl an schreibenden Operationen pro Sekunde? Wie schnell bzw. wie oft können und wollen Sie Ihre Reports erstellen? Wie gut passt Ihr logisches Datenmodell wirklich zum relationalen Modell?
Wenn Sie mit keinem dieser Aspekte oder ähnlich gelagerten Fragestellungen Probleme haben, dann haben Sie wahrscheinlich auch kein Big-Data-Problem. Stehen Sie aber vor einer oder allen diesen Herausforderungen, dann Sie sind sicherlich schon über den Begriff NoSQL gestolpert.
Der Begriff NoSQL2 ist keineswegs ein Imperativ, der zum vollständigen Boykott relationaler Datenbanksysteme aufruft. Er leitet sich ab von not only SQL, wobei SQL als Synonym für relationale Datenbankensysteme verwendet wird.
Die Grundidee ist die, dass man sich beim Umsetzen seiner konkreten Anforderungen (ob nun Big Data oder nicht) nicht im Vorhinein auf relationale Datenbanksysteme (RDBMS) beschränkt, nur weil man dies bei der Datenhaltung in den letzten Dekaden immer so getan hat. Riskieren Sie ruhig mal den Blick über den Tellerrand und setzen Sie das Datenbanksystem ein, das Ihr Problem optimal löst. Können Sie Ihr Problem am besten mit einer relationalen Datenbank lösen, dann tun Sie das auch und laufen nicht dem Hype hinterher.
Denn zum Einsatz von NoSQL-Technologien gehört Mut: Datenbankadministratoren haben wenig bis keine Erfahrung mit diesen neuartigen Technologien, Entwickler kennen die meist nichtstandardisierten APIs (noch) nicht. Das möchte ich mit diesem Buch in Bezug auf MongoDB ändern.
Entstanden sind die NoSQL-Datenbanken in der Web-2.0-Welt. Soziale Netzwerke wie Facebook, Twitter und Co. haben mit vielen Millionen, über den ganzen Globus verteilten Benutzern ganz andere Anforderungen an die Datenhaltung als das Bestandsführungssystem eines Versicherungsunternehmens. Es müssen sehr viele Daten sehr schnell gespeichert und abgerufen können. Dabei stoßen relationale Datenbanksysteme durchaus an ihre Grenzen, sowohl technologisch als auch vom Lizenzmodell her. Um eine fundierte Entscheidung bei der Auswahl der passenden NoSQL-Datenbank treffen zu können, ist natürlich – wie immer – Know-how gefragt, zumal sich in diesem Umfeld sehr viele Anbieter und auch Konzepte tummeln. Die wichtigsten Kategorien von NoSQL-Datenbanken3 sind:
Abbildung 1–1 zeigt einige der populärsten Vertreter:
Abb. 1–1 NoSQL-Datenbanken im Überblick
Relationale Datenbanksysteme sind Allzweckwaffen, die sich in der Regel auf eine große Menge von Problemen anwenden lassen. Im Gegensatz dazu stellen NoSQL-Datenbanken tendenziell eher Nischenlösungen dar, die bestimmte Probleme allerdings sehr viel besser lösen können.
MongoDB gehört zur Kategorie der sogenannten dokumentenorientierten Datenbanken. Damit werden wir uns im weiteren Verlauf dieses Buches sehr detailliert auseinandersetzen. Ein Dokument ist ein einzelner Datensatz, der im Prinzip aus einer geordneten Liste von Key-Value-Paaren besteht und als Werte auch Arrays und eingebettete Dokumente zulässt. Ein Dokument kann man gut im JSON-Format darstellen, z. B. so:
{ "name": "MongoDB", versionen: [ { "major": 2, "minor": 6 }, { "major": 2, "minor": 4 }, { "major": 2, "minor": 2 } ] }
Zur Speicherung verwendet MongoDB intern allerdings nicht das JSON-Format, sondern eine Abwandlung davon. Dazu aber später mehr. Mit diesem Datenformat adressiert MongoDB den Big-Data-Aspekt Variety: Ein Dokument eignet sich hervorragend zum Umgang mit semistrukturierten Daten, z. B. komplexen Vererbungshierarchien in OO-Sprachen. Aber auch für Volume und Velocity bietet MongoDB Lösungen an, auf die ich in den folgenden Kapiteln noch eingehen werde.
a. http://www.kchodorow.com/blog/2010/08/23/history-of-mongodb/
Den Big-Data-Aspekten Volume und Velocity ist in der Regel nur mit dem Einsatz von verteilten Systemen4 zu begegnen, in denen die Last und die Daten auf viele einzelne Rechnerknoten verteilt werden.
Aus dem CAP-Theorem (s. Kasten) folgt allerdings, dass man bei der Implementierung eines verteilten (Datenbank-)Systems nicht alle der Anforderungen an Konsistenz, Verfügbarkeit und Toleranz gegenüber Ausfällen gleichzeitig in vollem Maße erreichen kann.
Abb. 1–2 Veranschaulichung des CAP-Theorems
a. http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf
Die Auswirkungen des CAP-Theorems lassen sich gut an einem System veranschaulichen, das aus zwei Knoten besteht (s. Abb. 1–2). Das Netzwerk zwischen diesen Knoten soll gestört sein, sodass eine Partition des Systems in zwei Teile vorliegt, die sich gegenseitig nicht mehr erreichen können.
Erlaubt man nun einem der Knoten, seinen Zustand zu ändern (also im Falle eines Datenbanksystems schreibende Operationen auszuführen), wird das Gesamtsystem inkonsistent, man gibt also C auf. Will man die Konsistenz erhalten, muss der Knoten, dessen Zustand sich nicht ändert, sich als nicht verfügbar »abmelden« (da er sonst gegenüber den Clients einen nicht aktuellen Datenbestand ausliefern würde), womit man die Verfügbarkeit (A) des Systems auf nur noch einen Knoten reduziert. Nur wenn alle Knoten stets miteinander kommunizieren können, kann man dauerhaft C und A aufrecht erhalten, was dann offenbar im Widerspruch zu P steht.
Relationale Datenbanksysteme fallen in der Regel in die CA-Kategorie, da sie großen Wert auf die Konsistenz legen und auch hohe Verfügbarkeiten garantieren. Bei Systemen, die nur auf einem Knoten laufen, kann die Ausfallsicherheit durch den Kauf sehr teurer Hardware ebenfalls nah an die 100% gebracht werden. Fällt der eine Knoten aus, ist das System aber nicht mehr verfügbar. Bei Datenbank-Clustern verringert sich die Verfügbarkeit, da die Transaktionen zur Sicherstellung der Konsistenz auf mehreren Knoten eine längere Laufzeit haben.
Bei vielen NoSQL-Datenbanken kann man aufgrund der hohen Verteilung nicht auf die Partition Tolerance (P) verzichten. Da die Verfügbarkeit (A) ebenfalls einen sehr hohen Stellenwert einnimmt (da man grundsätzlich alle Anfragen an das System beantworten können möchte), bleibt nur der Ausweg, an der Konsistenz (C) zu sparen. Dies ist dann aber eine bewusste Design-Entscheidung beim Entwurf dieser Systeme und nicht dem Unwillen oder der Vergesslichkeit der Entwickler geschuldet.
Sie als Anwender solcher Technologien sollten sich aber stets bewusst sein, dass Sie sich nicht mehr auf Ihre bisherigen Konsistenzzusagen verlassen können!
Das ACID5-Prinzip, das Sie von relationalen Datenbanksystemen gewohnt sind, stellt in Zusammenhang mit Transaktionen6 Folgendes sicher:
Eine Transaktion wird nach dem Alles-oder-nichts-Prinzip entweder vollständig oder gar nicht ausgeführt. Wird eine atomare Transaktion abgebrochen, ist das System in einem unveränderten Zustand.
Eine Folge von Datenbankoperationen führt wieder zu einem konsistenten Zustand, sofern das System zuvor schon in einem konsistenten Zustand war. Hier bezieht sich die Konsistenz vorwiegend auf die inhaltliche und referenzielle Integrität7 des Datenbestandes.
Nebenläufig ausgeführte Transaktionen beeinflussen sich nicht gegenseitig.
Die Auswirkungen von Transaktionen müssen dauerhaft im System gespeichert werden, insb. auch bei Systemabstürzen.
Bitte beachten Sie, dass der Konsistenzbegriff aus dem CAP-Theorem und dem ACID-Prinzip nicht identisch sind, sondern sich auf verschiedenen Granularitätsebenen bewegen.
NoSQL-Datenbanken können (oder wollen) eine Konsistenz im Sinne des ACID-Prinzips oft nicht garantieren, gewährleisten dann aber einen anderen Grad an Konsistenz. Hier kommt der Begriff BASE8 ins Spiel:
Dieses zugegebenermaßen sehr konstruiert wirkende Akronym will den Gegensatz zu ACID (im Englischen Säure) und BASE (Lauge) aus der Chemie aufgreifen. Die beiden Begriffe Basically Available und Soft State sind nicht präzise definiert, greifen aber nach meiner Interpretation die Verfügbarkeit (A) des CAP-Theorems auf.
Unter Eventual Consistency wird relativ klar das C des CAP-Theorems adressiert, und zwar in dem Sinne, dass das verteilte System (nach einer möglichst kurzen Zeitspanne der Inkonsistenz) letztendlich wieder in einem konsistenten Zustand ist.
Die letztendliche Konsistenz möchte ich an einem konkreten Beispiel mit zwei Knoten erläutern. Schreibzugriffe sind nur an einem der Knoten, den wir Master nennen, erlaubt. Der Master repliziert den Datenbestand in regelmäßigen Abständen an den anderen Knoten, den wir Slave nennen. Lesezugriffe sind an beiden Knoten erlaubt. Nach einem Schreibvorgang auf dem Master sehen Clients, die vom Slave lesen, die neuen oder geänderten Daten nicht sofort. Erst nach Abschluss der Replikation, die ggf. asynchron arbeitet, ist der Slave wieder auf dem aktuellen Stand.
Mit MongoDB können Sie keine Transaktionen fahren, die mehr als ein Dokument umfassen. Selbst bei Operationen auf nur einem Dokument ist kein explizites Rollback möglich. Immerhin sind Änderungen an einem Dokument aber atomar im Sinne des ACID-Prinzips. Da Sie ganze Objektnetze in einem Dokument unterbringen können, sind dokumentenübergreifende Transaktionen oft auch gar nicht notwendig.
In dieser Einleitung haben Sie die Herausforderungen kennengelernt, die es im Big-Data-Umfeld zu meistern gilt. Sie haben ferner die Grundidee hinter der NoSQL-Bewegung verstanden und wissen, dass Sie insbesondere bei der Konsistenz umdenken müssen, wenn Sie MongoDB einsetzen.
Was Sie dabei beachten müssen, damit Sie dennoch keine Daten verlieren, werde ich Ihnen im Rest des Buches zeigen. Lassen Sie uns also jetzt direkt durchstarten in die Welt abseits des relationalen Pfades, in eine Welt ohne ACID, in die Welt von MongoDB ...
In diesem Kapitel zeige ich Ihnen, wie schnell Sie einen MongoDB-Server installieren, starten und benutzen können. Sie werden Testdaten importieren und Ihre ersten Abfragen absetzen.
Die jeweils aktuelle Version von MongoDB kann auf der Homepage heruntergeladen werden: http://www.mongodb.org/downloads. Sie sollten die stabile Produktionsversion verwenden. Ich werde die Installation exemplarisch für Windows durchführen, Anleitungen für andere Betriebssysteme können Sie der Online-Dokumentation1 entnehmen.
Nachdem die passende Archivdatei heruntergeladen ist, entpacke ich den Inhalt in ein Verzeichnis meiner Wahl, z. B. C:\nosql. Dort befindet sich nun ein neues Verzeichnis, dessen Name mit mongodb gefolgt von Betriebssystem- und Versionsinfos beginnt, z. B.:
c:\nosql\mongodb-win32-x86_64-2008plus-2.x
Auf dieses Verzeichnis werde ich mich im Folgenden als MONGO_HOME beziehen.
Um den Server-Prozess zu starten, öffnen Sie eine Kommandozeilen-Shell und wechseln in das Verzeichnis MONGO_HOME\bin. Dort befinden sich alle ausführbaren Programme, die wir zum Betrieb und zur Administration eines MongoDB-Systems benötigen. Zunächst müssen wir noch ein neues Verzeichnis anlegen, in dem MongoDB seine Daten speichert:
rem Windows mkdir \data # Unix mkdir /data
Anschließend starten wir den Server mit
mongod --dbpath /data
Auf der Konsole sollten nun in etwa folgende Log-Ausgaben zu sehen sein:
Sat Dec 01 14:15:40 [initandlisten] MongoDB starting : pid=5616 port=27017 dbpath=/data 64-bit host=localhost Sat Dec 01 14:15:40 [initandlisten] db version v2.x, pdfile version 4.5 Sat Dec 01 14:15:40 [initandlisten] git version: f5e83eae9cfbec7fb7a071321928f00d1b0c5207 Sat Dec 01 14:15:40 [initandlisten] build info: windows sys.getwindowsversion(major=6, minor=1, build=7601, platform=2, service_pack='Service Pack 1') BOOST_LIB_VERSION=1_49 Sat Dec 01 14:15:40 [initandlisten] options: { dbpath: "/data", rest: true } Sat Dec 01 14:15:40 [initandlisten] journal dir=/data/journal Sat Dec 01 14:15:40 [initandlisten] recover : no journal files present, no recovery needed Sat Dec 01 14:15:40 [initandlisten] waiting for connections on port 27017
Ohne weitere Konfiguration verwendet ein MongoDB-Server als Default den TCP/IP-Port 27017 für eingehende Verbindungen. Wenn Sie den Server mit der Kommandozeilenoption
$ mongod --httpinterface --rest
starten, können Sie 1000 Ports höher, also unter folgender URL:
http://localhost:28017
eine einfache, HTML-basierte Administrationsoberfläche erreichen (s. Abb. 2–1)
Abb. 2–1 Interface zur Administration
Dort finden sich Informationen über verbundene Clients, Datenbankoperationen und aufgetretene Locks, auf die wir im weiteren Verlauf des Buchs näher eingehen werden.
Im weiteren Verlauf werden wir die sogenannte Mongo Shell verwenden. Dabei handelt es sich um einen Kommandozeilen-Client, mit dem wir den Server administrieren und auch Daten manipulieren können.
Als Benutzer erhalten Sie einen Prompt, auf dem Sie JavaScript2-Befehle und -Skripte ausführen können. Dabei können Sie auf eine Reihe bestehender Java-Script-Objekte zugreifen, die jeweils bestimmte Datenbankkonstrukte repräsentieren. Aus Sicht des Servers verhält sich die in C++ implementierte Mongo Shell wie jeder andere Datenbank-Client.
Gestartet wird das Ganze im Verzeichnis MONGO_HOME/bin:
$ mongo3
Da der zuvor gestartete Server auf dem Standardport 27017 horcht, müssen wir keine weiteren Verbindungsparameter angeben. Danach werden wir von der Mongo Shell wie folgt begrüßt:
MongoDB shell version: 2.x connecting to: test >
Wenn wir beim Start der Mongo Shell keinen Namen einer Datenbank angeben, verwenden wir standardmäßig eine zunächst leere Datenbank namens test. Setzen Sie zum Einstieg ein paar einfache JavaScript-Befehle ab:
> var a = 2 > b = a + 2 4 > for (i=0; i<5; i++) { print(i) } 0 1 2 3 4
Ein MongoDB-Server verwaltet mehrere logische Datenbanken. Einen Überblick über alle Datenbanken verschaffen wir uns mit dem Befehl
> show dbs local 0.078125GB
In einem frisch aufgesetzten System sehen wir nur die stets existierende Datenbank local.
Eine Übersicht über weitere Befehle erhalten Sie mit dem Kommando help().
Einzelne Datensätze, die in MongoDB Dokumente heißen, werden innerhalb logischer Namensräume organisiert, den sogenannten Collections. Eine Collection entspricht in etwa einer Tabelle aus einem relationalen Datenbanksystem, ist aber nicht identisch damit. Collections müssen nicht explizit angelegt werden, sie werden bei der ersten Verwendung automatisch erzeugt. In unserer Datenbank test gibt es zunächst noch keine Collections:
> show collections >
Dokumente werden innerhalb der Mongo Shell im JSON4-Format repräsentiert. Das JSON-Format ist ein leichtgewichtiges, textbasiertes Format, in dem man ein Objekt gut lesbar darstellen kann, z. B.:
> doc = { hello: "MongoDB" }
Im obigen Beispiel ist doc ein Dokument mit einem Feld namens hello, das den String-Wert "MongoDB" hat.
Wir werden in Kapitel 3 noch genauer auf Collections und Dokumente eingehen.
Ich werde nun in der Collection dokumente einige Dokumente speichern, manipulieren und suchen. Mit
> db.dokumente.insert( doc ) > db.dokumente.insert( {a: 1, b: "zwei" } ) > db.dokumente.insert( {a: 2, b: "drei" } )
ist das Speichern bereits erledigt. Das JavaScript-Objekt db repräsentiert in der Mongo Shell die aktuell selektierte Datenbank. Collections werden automatisch zu Attributen dieses Datenbankobjekts und sind daher über den Ausdruck db.name oder db["name"] zugreifbar. Mit dem ersten schreibenden Zugriff auf die Collection dokumente wurde diese angelegt, wie die Auflistung aller Collections nun zeigt:
> show collections dokumente system.indexes
Mit db.dokumente.help() erhalten Sie Hilfe zu den Befehlen der Collection.
Neben unserer Collection dokumente ist auch eine weitere Collection system. indexes entstanden, die Informationen über Indizes enthält (mehr dazu in Kap. 6). Wir können nun die zuvor eingefügten Dokumente z. B. zählen oder auch suchen:
> db.dokumente.count() 3 > db.dokumente.find() { "_id" : ObjectId("50ba1112b9b2400412cbe6bd"), "hello" : "MongoDB" } { "_id" : ObjectId("50ba1112b9b2400412cbe6be"), "a" : 1, "b" : "zwei" } { "_id" : ObjectId("50ba112eb9b2400412cbe6bf"), "a" : 2, "b" : "drei" }
Dabei fällt auf, dass beim Schreiben ein Feld _id erzeugt wurde, mit dem das Dokument eindeutig identifiziert werden kann. Das Feld _id wird automatisch ergänzt, wenn Sie es nicht selbst angeben.
Da man oft nicht an allen Dokumenten innerhalb einer Collection interessiert ist, kann man der Methode find(...) Suchkriterien und Feldgruppen in Form von Dokumenten mitgeben, um die Treffermenge einzuschränken, z. B.:
> db.dokumente.find( {a: 2}, {_id: 0} ) { "a" : 2, "b" : "drei" }
In diesem Beispiel schränken wir mit dem ersten Parameter {a: 2} die Menge der gefundenen Dokumente auf solche ein, bei denen das Feld a den Wert 2 besitzt. Mit dem optionalen zweiten Parameter schränken wir die Menge der zurückgelieferten Felder innerhalb eines Dokuments ein, indem wir für Felder, die wir ignorieren möchten, eine 0 angeben. (In SQL würden Sie an dieser Stelle eine konkrete Spaltenliste anstatt von * beim SELECT angeben.)
Neben dem einfachen Feldvergleich auf Identität gibt es noch viele weitere sog. Query-Operatoren, die ich ausführlich in Kapitel 6 erklären werde.
Im Gegensatz zu relationalen Datenbanken kann ein einzelnes Feld eines Dokuments aber auch Arrays oder eingebettete Unterobjekte enthalten:
> db.autoren.insert( { titel: "MongoDB: Ein praktischer Einstieg", autoren:[ {name: "N.N."}, {name: "Tobias Trelle"} ] } )
Eine anschließende Suche mit Formatierung macht die Struktur deutlicher:
> db.autoren.find().pretty() { "_id" : ObjectId("50ba1bafb9b2400412cbe6c0"), "titel" : "Praxisbuch MongoDB", "autoren" : [ { "name" : "N.N" }, { "name" : "Tobias Trelle" } ] }
Wenn wir nun ein bestimmtes Objekt verändern wollen, können wir die Methode update(...) verwenden:
> db.dokumente.update( {a: 2}, {b: "vier"} )
Intuitiv würde man nun vielleicht vermuten, dass im Dokument mit a = 2 der Wert für b auf "vier" gesetzt wurde. Dies ist aber nicht der Fall, wie eine anschließende Suche zeigt (mit leerem Suchkriterien-Dokument {} werden alle Dokumente gefunden):
> db.dokumente.find({}, {_id: 0}) { "a" : 1, "b" : "zwei" } { "b" : "vier" }
Die Semantik der update(...) -Methode ist so, dass der zweite Parameter (hier {b: "vier"}) das bestehende Dokument vollständig ersetzt und nicht nur die darin vorkommenden Fehler manipuliert!
Partielle Updates und noch einiges mehr sind aber dennoch möglich und werden in Kapitel 7 ausführlich behandelt.
Einzelne Dokumente werden mit remove(...) gelöscht:
> db.dokumente.remove( {a: 1} ) > db.dokumente.find({}, {_id: 0}) { "b" : "vier" }
Um alle Dokumente einer Collection zu löschen, ruft man remove() ohne Parameter auf. Dies kann bei großen Datenmengen sehr lange dauern, da jedes Dokument einzeln gelöscht wird, was z. B. auch eine Aktualisierung von Indizes auslösen kann. Schneller ist man hier mit:
> db.dokumente.drop() true > show collections system.indexes
Mit drop() wird die Collection vollständig gelöscht, inklusive aller bestehenden Indizes.
Die bisherigen Beispiele mögen Ihnen zu Recht trivial vorkommen. Im weiteren Verlauf des Buchs werden wir auf eine größere und komplexere Datenmenge zurückgreifen. Es handelt sich um einen Auszug aus öffentlich zugänglichen Tweets der Twitter-Plattform. Diese Daten können Sie von
https://github.com/ttrelle/mongodb-buch/blob/master/data/tweets.zip?raw=true
herunterladen und ins Verzeichnis MONGO_HOME entpacken. Der Inhalt des Archivs ist eine Datei im BSON-Format, die gut 50.000 Dokumente beinhaltet, die wir gleich in eine Collection einspielen werden.
Beenden Sie dazu nun die Mongo Shell mit dem Befehl exit. Anschließend rufen Sie im Verzeichnis MONGO_HOME\bin den folgenden Befehl auf, um die Daten in eine Datenbank namens twitter in eine Collection namens tweets zu importieren:
$ mongorestore -d twitter../tweets.bson connected to: 127.0.0.1 2014-02-27T08:30:07.181+0100 tweets.bson 2014-02-27T08:30:07.185+0100 going into namespace [twitter.tweets] 2014-02-27T08:30:07.187+0100 tweets.metadata.json not found. Skipping. 2014-02-27T08:30:10.002+0100 Progress: 76560817/85711256 89% (bytes) 53641 objects found
Die Ausführungszeit des Importvorgangs kann in Abhängigkeit von Ihrem System variieren. Sobald die Daten importiert sind, rufen wir wieder die Mongo Shell auf und wechseln in die Twitter-Datenbank, um uns die Anzahl der Tweets anzuzeigen:
$ mongo MongoDB shell version: 2.x connecting to: test > use twitter switched to db twitter > show collections system.indexes tweets > db.tweets.count() 53641
Wenn wir nun mittels
> db.tweets.find()
nach allen importierten Tweets suchen, gibt die Mongo Shell einen Wust von Dokumenten aus. Die Anzahl der angezeigten Dokumente wird per Default auf 20 beschränkt. Diese Anzahl wird bestimmt durch:
> DBQuery.shellBatchSize 20
Um die jeweils nächste Tranche der Ergebnismenge anzuzeigen, geben Sie bitte den Befehl it ein. Um Dokumente lesbarer anzuzeigen, verwenden Sie diesen Befehl:
> db.tweets.find().pretty()
Probieren Sie einfach mal ein paar Suchen nach verschiedenen Kriterien aus. Die Hilfe zur find()- Methode unterstützt Sie dabei.
> db.tweets.find().help()
Alle weiteren Details zu Queries und auch zur Indexierung finden Sie in Kapitel 6.
Eines der Alleinstellungsmerkmale von MongoDB ist die sogenannte Schemafreiheit, auf die ich in Kapitel 8 noch genauer eingehen werde. Zum Einstieg will ich Ihnen anhand eines einfachen Beispiels die Möglichkeiten aufzeigen, die Sie ohne ein starres Schema haben.
Sie können z. B. einen Produktkatalog mit den unterschiedlichsten Produkttypen innerhalb einer Collection realisieren:
> use shop switched to db shop > db.produkte.insert({ typ: "dvd", titel: "Lost Highway", regie: "David Lynch" }) > db.produkte.insert({ typ: "cd", titel: "Highway to Hell", band: "AC/DC" })
Die Suche nach gemeinsamen Feldern der unterschiedlichen Produkte ist ganz einfach:
> db.produkte.find({titel: /Highway/}) { "_id" : ObjectId("530eefa913109e3002c125de"), "typ" : "dvd", "titel" : "Lost Highway", "regie" : "David Lynch" } { "_id" : ObjectId("530eefe313109e3002c125df"), "typ" : "cd", "titel" : "Highway to Hell", "band" : "AC/DC" }
Eine Einschränkung auf bestimmte Produktarten ist dabei möglich:
> db.produkte.find({titel: /Highway/, typ: "cd"}) ...
Das gilt ebenso für die Suche nach Feldern, die es nur für bestimmte Produkte gibt:
> db.produkte.find({regie: "David Lynch"}) ...
Das Beispiel ist zugegebenermaßen trivial, deutet aber die Mächtigkeit an, die die Schemafreiheit beim Umgang mit semistrukturierten Daten bietet. Hier kristallisiert sich einer der Hauptvorteile von MongoDB heraus, der insbesondere auch den Big-Data-Aspekt Variety adressiert.
In diesem Kapitel haben Sie gesehen, wie einfach der Einstieg in MongoDB ist. Nach den ersten Schritten haben Sie bereits Daten importiert und die Vorteile der Schemafreiheit kennengelernt. Im nächsten Kapitel werde ich Ihnen die technologischen Grundlagen für all das näher erklären.
Nachdem Sie im letzten Kapitel die ersten praktischen Erfahrungen mit MongoDB und der Mongo Shell gesammelt haben, werden wir nun näher auf die dahinter liegenden Konzepte und Begriffe eingehen und die technischen Grundlagen beleuchten, auf denen MongoDB basiert.
Kapitel 6