Upload
uwe-seiler
View
1.847
Download
0
Embed Size (px)
DESCRIPTION
Der Talk wurde am 25.09.2013 auf der Java User Group Frankfurt gehalten und gibt einen Überblick und Einstieg in MongoDB aus der Sicht eines Java-Programmierers. Dabei werden folgende Themen behandelt: - Buzzword Bingo: NoSQL, Big Data, Horizontale Skalierung, CAP-Theorem, Eventual Consistency - Übersicht über MongoDB - Datenmanipulation: CRUD, Aggregation Framework, Map/Reduce - Indexing - Konsistenz beim Schreiben und Lesen von Daten - Java API & Frameworks
Citation preview
Uwe Seileruweseiler
MongoDB für Java-Programmierer
About me
Big Data Nerd
TravelpiratePhotography Enthusiast
Hadoop Trainer MongoDB Author
About usis a bunch of…
Big Data Nerds Agile Ninjas Continuous Delivery Gurus
Enterprise Java Specialists Performance Geeks
Join us!
Agenda
• Buzzword Bingo
• Überblick über MongoDB
• Datenmanipulation
• Indexing
• Konsistenz beim Schreiben und Lesen von Daten
• Java API & Frameworks
Buzzword Bingo
NoSQL
Klassifizierung von NoSQL
Key-Value StoresK V
K V
K V
K V
K V
11 1 1
1 11 11
11
Column Stores
Graph Databases Document Stores
_id_id_id
Big Data
Meine Lieblingsdefinition
Die klassische Definition
• The 3 V’s of Big Data
•VarietyVolume Velocity
«Big Data» != Hadoop
Horizontale Skalierung
Vertikale Skalierung
RAMCPU
Storage
RAMCPU
Storage
Vertikale Skalierung
RAMCPU
Storage
Vertikale Skalierung
Horizontale Skalierung
RAMCPU
Storage
Horizontale Skalierung
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
Horizontale Skalierung
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
RAMCPU
Storage
Das Problem mit der
Verteilung
Partition
ToleranceTrotz
Knotenausfall funktioniert das System
Partition
ToleranceTrotz
Knotenausfall funktioniert das System
Das CAP Theorem
Consistency
Alle Knoten haben
jederzeit die gleichen
Informationen
Consistency
Alle Knoten haben
jederzeit die gleichen
Informationen
Availability
Jede Anfrage bekommt eine
Antwort
Availability
Jede Anfrage bekommt eine
Antwort
Consistency
Alle Knoten haben
jederzeit die gleichen
Informationen
Consistency
Alle Knoten haben
jederzeit die gleichen
Informationen
Availability
Jeder Client kann immer
schreiben und lesen
Availability
Jeder Client kann immer
schreiben und lesen
Überblick über NoSQL Systeme
Partition Tolerance
Trotz Knotenausfall
funktioniert das System
Partition Tolerance
Trotz Knotenausfall
funktioniert das System
Das Problem mit der
Konsistenz
ACID
vs.
BASE
ACID vs. BASE
Atomicity
Consistency
Isolation
Durability1983
RDBMS
ACID ist ein gutes Konzept, aber es ist
kein in Stein gemeißeltes Gesetz!
ACID vs. BASE
BasicallyAvailable
Soft State
Eventually consistent2008
NoSQL
ACID vs. BASE
ACID
- Starke Konsistenz- Isolation- Zwei-Phasen-Commit- Komplexe Entwicklung - Zuverlässiger
BASE
- Schwache Konsistenz- Verfügbarkeit- "Fire-and-forget"- Leichtere Entwicklung- Schneller
ACID vs. BASE
Überblick über MongoDB
MongoDB ist eine …
• Dokumenten-basierte
• Open Source
• Performante
• Flexible
• Skalierbare
• Hochverfügbare
• Funktionsreiche…Datenbank
Dokumenten-basierte Datenbank
• Nicht im Sinne einer Datenbank für PDF-oder Worddokumente…
Open Source Datenbank
• MongoDB ist ein Open Source Projekt
• Auf GitHub– https://github.com/mongodb/mongo
• Steht unter der AGPL Lizenz
• Gestartet und gesponsert von MongoDB Inc. (früher: 10gen)
• Kommerzielle Lizenzen sind verfügbar
• Jeder darf mitmachen!– https://jira.mongodb.org
Daten-lokalität
Performance
In-Memory Caching
In-Place Updates
Flexibles Schema
RDBMS MongoDB
{
_id :
ObjectId("4c4ba5e5e8aabf3"),
employee_name: "Dunham, Justin",
department : "Marketing",
title : "Product Manager, Web",
report_up: "Neray, Graham",
pay_band: “C",
benefits : [
{ type : "Health",
plan : "PPO Plus" },
{ type : "Dental",
plan : "Standard" }
]
}
Skalierbarkeit
Auto-Sharding
• Erhöhung der Kapazität wenn nötig
• Ausgelegt für Commodity Hardware
• Funktioniert mit Cloud-Architekturen
Hochverfügbarkeit
• Automatische Replikation und Failover
• Unterstützung für mehrere Datenzentren
• Ausgelegt auf möglichst einfachen Betrieb
• Beständigkeit und Konsistenz der Daten
MongoDB Architektur
Reichhaltige Abfragen
Aggregation Framework
Map/Reduce
MongoDB DataGroup(k)
Sort(k)
Finalize(k, v)
Map()
emit(k,v)
Reduce(k, values)
Shard 1
Shard 2
Shard n
…
Geoinformationen
Treiber & Shell
Shell zur Interaktion mitder Datenbank
Treiber verfügbar fürdie populärstenProgrammiersprachenund Frameworks
> db.collection.insert({product:“MongoDB”, type:“Document Database”})> > db.collection.findOne(){
“_id” : ObjectId(“5106c1c2fc629bfe52792e86”),“product” : “MongoDB”“type” : “Document Database”
}
Java
Python
Perl
Ruby
Haskell
JavaScript
Indeed.com Trends
Top Job Trends
1.HTML 5
2.MongoDB
3.iOS
4.Android
5.Mobile Apps
6.Puppet
7.Hadoop
8.jQuery
9.PaaS
10.Social Media
NoSQL TrendsLinkedIn Job Skills
MongoDB
Competitor 1
Competitor 2
Competitor 3
Competitor 4
Competitor 5
All Others
Google Search
MongoDB
Competitor 1
Competitor 2
Competitor 3
Competitor 4
Jaspersoft Big Data Index
Direct Real-Time Downloads
MongoDB
Competitor 1
Competitor 2
Competitor 3
Datenmanipulation
RDBMS MongoDBTabelle / View ➜ CollectionZeile ➜ DokumentIndex ➜ IndexJoin ➜ Eingebettetes
DokumentFremdschlüssel ➜ Referenziertes
DokumentPartition ➜ Shard
Terminologie
Let’s have a look…
// Anzeigen aller Datenbanken> show dbsdigg 0.078125GBenron 1.49951171875GB
// Wechsel in eine Datenbank> use blog
// Erneutes Anzeigen aller Datenbanken> show dbsdigg 0.078125GBenron 1.49951171875GB
Anlegen einer Datenbank
// Anzeigen aller Collections> show collections
// Einfügen eines Benutzers> db.user.insert(
{ name : “Sheldon“, mail : “[email protected]“ }
)
Anlegen einer Collection I
Beim Einfügen erfolgt kein Feedback über den Erfolg der Operation, Abfrage über:
db.runCommand( { getLastError: 1} )
// Anzeigen aller Collections> show collectionssystem.indexesuser
// Anzeigen aller Datenbanken> show dbsblog 0.0625GBdigg 0.078125GBenron 1.49951171875GB
Anlegen einer Collection II
Datenbank und Collection werden automatisch beim ersten Insert anlegt.
// Anzeigen des ersten Dokuments> db.user.findOne(){
"_id" : ObjectId("516684a32f391f3c2fcb80ed"),"name" : "Sheldon","mail" : "[email protected]"
}
// Alle Dokumente einer Collection anzeigen> db.user.find(){
"_id" : ObjectId("516684a32f391f3c2fcb80ed"),"name" : "Sheldon","mail" : "[email protected]"
}
Lesen aus einer Collection
// Filtern von bestimmten Dokumenten> db.user.find( { name : ”Penny” } ){
"_id" : ObjectId("5166a9dc2f391f3c2fcb80f1"),"name" : "Penny","mail" : "[email protected]"
}
// Nur bestimmte Felder anzeigen> db.user.find( { name : ”Penny” },
{_id: 0, mail : 1} )
{ "mail" : "[email protected]" }
Filtern von Dokumenten
_id
• _id ist der primäre Schlüssel in MongoDB
• Index auf _id wird automatisch erzeugt
• Wenn nicht anders angegeben, handelt es sich dabei um eine ObjectId
• _id kann auch selbst beim Einfügen von Dokumenten vergeben werden, jeder einzigartige unveränderbare Wert kann dabei verwendet werden
ObjectId
• Eine ObjectId ist ein spezieller 12 Byte Wert
• Ihre Einzigartigkeit über den gesamten Cluster ist durch die Zusammensetzung garantiert:
ObjectId("50804d0bd94ccab2da652599")|-------------||---------||-----||----------|
ts mac pid inc
// Benutzen eines Cursors für die Dokumente> var myCursor = db.user.find( )
// Nächstes Dokument holen und Mail anzeigen> var myDocument =
myCursor.hasNext() ? myCursor.next() : null;
> if (myDocument) { printjson(myDocument.mail); }
// Restliche Dokumente anzeigen> myCursor.forEach(printjson);
Cursor
In der Shell werden per Default 20 Dokumente angezeigt.
// Oder-Verknüpfung> db.user.find(
{$or : [ { name : “Sheldon“ }, { mail : [email protected] }
] } )
// Und-Verknüpfung> db.user.find(
{$and : [ { name : “Sheldon“ }, { mail : [email protected] }
] } )
Logische Verknüpfungen
// Sortieren von Dokumenten> db.user.find().sort( { name : 1 } ) // Aufsteigend> db.user.find().sort( { name : -1 } ) // Absteigend
// Ergebnismenge limitieren> db.user.find().limit(3)
// Ergebnisdokumente überspringen> db.user.find().skip(2)
// Kombination der Methoden> db.user.find().skip(2).limit(3)
Ergebnismengen anpassen
// Update der Mail-Adresse (So bitte nicht!)> db.user.update( { name : “Sheldon“ },
{ mail : “[email protected]“ } )
// Anzeige des Updatesdb.user.findOne(){
"_id" : ObjectId("516684a32f391f3c2fcb80ed"),"mail" : "[email protected]"
}
Update von Dokumenten I
Aufpassen beim Update von Dokumenten!
// Löschen des Dokuments> db.user.remove(
{ mail : “[email protected]“ } )
// Löschen aller Dokumente> db.user.remove()
// Löschen von Dokumenten mittels Bedingung> db.user.remove(
{ mail : /.*mother.com$/ } )
// Löschen nur des ersten passenden Dokuments> db.user.remove( { mail : /.*.com$/ }, true )
Löschen von Dokumenten
// Update der Mail-Adresse (Jetzt aber richtig!)> db.user.update( { name : “Sheldon“ },
{ $set : { mail : “[email protected]“
} } )
// Anzeige des Updatesdb.user.find(name : “Sheldon“){
"_id" : ObjectId("5166ba122f391f3c2fcb80f5"),"mail" : "[email protected]","name" : "Sheldon"
}
Update von Dokumenten II
// Hinzufügen eines Arrays> db.user.update( {name : “Sheldon“ },
{ $set : {enemies : [ { name : “Wil Wheaton“ },
{ name : “Barry Kripke“ } ]
} } )
// Hinzufügen eines Wertes zum Array> db.user.update( { name : “Sheldon“},
{ $push : {enemies : { name : “Leslie Winkle“}
} } )
Hinzufügen zu Arrays
// Löschen eines Wertes aus dem Array> db.user.update( { name : “Sheldon“ },
{$pull : {enemies : {name : “Barry Kripke“ }
} } )
// Löschen des kompletten Feldes> db.user.update( {name : “Sheldon“},
{$unset : {enemies : 1}})
Löschen aus Arrays
// Hinzufügen eines Subdokuments> db.user.update( { name : “Sheldon“}, {
$set : { mother :{ name : “Mary Cooper“, residence : “Galveston, Texas“, religion : “Evangelical Christian“ }}})
{"_id" : ObjectId("5166cf162f391f3c2fcb80f7"),"mail" : "[email protected]","mother" : {
"name" : "Mary Cooper","residence" : "Galveston, Texas","religion" : "Evangelical Christian"
},"name" : "Sheldon"
}
Einfügen eines Subdokuments
// Abfrage des Namens der Mutter> db.user.find( { name : “Sheldon“},
{“mother.name“ : 1 } )
{"_id" : ObjectId("5166cf162f391f3c2fcb80f7"),"mother" : {
"name" : "Mary Cooper"}
}
Abfragen auf Subdokumenten
Zusammengesetzte Feldnamen müssen in “…“ stehen!
Für Felder:$inc$rename$set$unset
Bitweise:$bit
Isolation:$isolated
Übersicht über alle Update-Operatoren
Für Arrays:$addToSet$pop$pullAll$pull$pushAll$push$each (Modifier)$slice (Modifier)$sort (Modifier)
Createhttp://docs.mongodb.org/manual/core/create/
Readhttp://docs.mongodb.org/manual/core/read/
Updatehttp://docs.mongodb.org/manual/core/update/
Deletehttp://docs.mongodb.org/manual/core/delete/
Dokumentation
Das Aggregation Framework
• Wurde eingeführt, um Aggregationen ohne Map/Reduce berechnen zu können
• Framework von Methoden & Operatoren– Deklarativ– Kein eigener JavaScript-Code mehr nötig– Framework kann erweitert werden
• Implementiert in C++– Overhead der JavaScript-Engine wird vermieden– Höhere Performance
Aggregation Pipeline
{document}
Pipeline Operator
Pipeline Operator
Pipeline Operator
Ergebnis{
sum: 337avg: 24,53min: 2max : 99
}
Aggregation Pipeline
• Verarbeitet einen Strom von Dokumenten– Eingabe ist eine Collection– Ausgabe ist ein Ergebnisdokument
• Aneinanderreihung von Pipeline-Operatoren– Jede Stufe filtert oder transformiert die Dokumente– Ausgabedokumente einer Stufe sind die Eingabe-
dokumente der nächsten Stufe
> db.tweets.aggregate(
{ $pipeline_operator_1 },
{ $pipeline_operator_2 },
{ $pipeline_operator_3 },
{ $pipeline_operator_4 },
...
);
Aufruf
// Alte Bekannte*
$match
$sort
$limit
$skip
Pipeline Operatoren
// Neue Freunde
$project
$group
$unwind
* Aus der Abfragefunktionalität
// Collection mit Tweets{
"_id" : ObjectId("4fb9fb91d066d657de8d6f39"),"text" : "I can't wait for #BoardwalkEmpire","in_reply_to_status_id" : null,"retweet_count" : null,"contributors" : null,"created_at" : "Thu Sep 02 18:11:24 +0000 2010",
…"user" : {
"friends_count" : 204,…
"followers_count" : 24,"id" : 64054560,…
},…}
Aggregation Framework I
// Finde die Top-3-Twitterer nach Followern
> db.tweets.aggregate({ $project : {name : "$user.name",
follower_count : "$user.followers_count"}},{ $group : {_id : {name : "$name"},
follower_count : {$max : "$follower_count"}}},{ $sort : {follower_count : -1}},{ $limit: 3}
);
Aggregation Framework II
// Finde die Top-3-Links aus Tweets
> db.tweets.aggregate({ $project : {_id: 0, inhalt_des_tweets :
"$text", links : "$entities.urls.url" } },{ $unwind : "$links" },{ $group : { _id : "$links",
anzahl : {$sum : 1} } },{ $sort : {anzahl : -1} },{ $limit : 3 }
);
Aggregation Framework III
Was ist Map/Reduce?
• Programmiermodel aus der funktionalen Welt
• Framework zur– parallelen Verarbeitung– von großen Datenmengen– mittels verteilter Systeme
• Populär geworden durch Google– Wird zur Berechnung des Suchindex verwendet,
welcher Seiten zu Keywords zuordnet (Page Rank)– http://research.google.com/archive/mapreduce.html
Map/Reduce mit MongoDB
MongoDB Daten group(k)
sort(k)
finalize(k, v)
k, v
map()emit(k,v)
• Iteriert über alle Dokumente
reduce(k, values)k, v
• Input = Output• Kann mehrfach laufen
Shard 1
Shard 2
Shard n
…
Word Count: Problemstellung
INPUT MAPPER GROUP/SORT REDUCER OUTPUT
{ MongoDB
uses MapReduce
}
{There is a
map phase}
{There is a
reduce phase
}
a: 2is: 2
map: 1
mapreduce: 1mongodb: 1
phase: 2
reduce: 1there: 2uses: 1
Problem:Wie häufig kommt ein Wort in allen Dokumenten vor?
// Beispiel: Twitter-Datenbank mit Tweets> db.tweets.findOne(){
"_id" : ObjectId("4fb9fb91d066d657de8d6f38"),"text" : "RT @RevRunWisdom: The bravest thing that men do is
love women #love","created_at" : "Thu Sep 02 18:11:24 +0000 2010",
…
"user" : {"friends_count" : 0,"profile_sidebar_fill_color" : "252429","screen_name" : "RevRunWisdom","name" : "Rev Run",
},…
Word Count: Tweets
// Map Funktion mit Bereinigung der Datenmap = function() {
this.text.split(' ').forEach(function(word) {
// Entfernen von Whitespaceword = word.replace(/\s/g, "");
// Entfernen alle Non-Word-Charactersword = word.replace(/\W/gm,"");
// Finally emit the cleaned up wordif(word != "") {
emit(word, 1)}
});};
Word Count: Map Funktion
// Reduce Funktionreduce = function(key, values) {
return values.length;};
Word Count: Reduce Funktion
// Anzeigen des Ergebnisses in der Konsole> db.tweets.mapReduce(map, reduce, { out : { inline : 1 } } );
// Speichern des Ergebnisses in einer Collection> db.tweets.mapReduce(map, reduce, { out : "tweets_word_count"} );
{"result" : "tweets_word_count","timeMillis" : 19026,"counts" : {
"input" : 53641,"emit" : 559217,"reduce" : 102057,"output" : 131003
},"ok" : 1,
}
Word Count: Aufruf
// Ausgeben der 10 häufigsten Wörter in Tweets> db.tweets_word_count.find().sort({"value" : -1}).limit(10)
{ "_id" : "Miley", "value" : 31 }{ "_id" : "mil", "value" : 31 }{ "_id" : "andthenihitmydougie", "value" : 30 }{ "_id" : "programa", "value" : 30 }{ "_id" : "Live", "value" : 29 }{ "_id" : "Super", "value" : 29 }{ "_id" : "cabelo", "value" : 29 }{ "_id" : "listen", "value" : 29 }{ "_id" : "Call", "value" : 28 }{ "_id" : "DA", "value" : 28 }
Word Count: Ergebnis
Indexing
Indexe in MongoDB sind B-Trees
Abfragen, Einfügen und Löschen: O(log(n))
Fehlende oder nicht optimale Indexe sind
das häufigste vermeidbare MongoDB Performance-Problem
// Anlegen eines Index, wenn er noch nicht existiert> db.recipes.createIndex({ main_ingredient: 1 })
// Der Client merkt sich den Index und wirft keinen Fehler> db.recipes.ensureIndex({ main_ingredient: 1 })
* 1 für aufsteigend, -1 für absteigend
Wie lege ich Indexe an?
// Mehrere Felder (Compound Key Indexes)> db.recipes.ensureIndex({
main_ingredient: 1,calories: -1
})
// Arrays mit Werten (Multikey Indexes){
name: 'Chicken Noodle Soup’,ingredients : ['chicken', 'noodles']
}
> db.recipes.ensureIndex({ ingredients: 1 })
Was kann indexiert werden?
// Subdokumente{
name : 'Apple Pie', contributor: {
name: 'Joe American',id: 'joea123'
}}
db.recipes.ensureIndex({ 'contributor.id': 1 })
db.recipes.ensureIndex({ 'contributor': 1 })
Was kann indexiert werden?
// Auflisten aller Indexe einer Collection
> db.recipes.getIndexes()
> db.recipes.getIndexKeys()
// Löschen eines Index
> db.recipes.dropIndex({ ingredients: 1 })
// Löschen und Neuerzeugung aller Indexe
db.recipes.reIndex()
// Defaultindex auf _id
Wie verwalte ich Indexe?
Weitere Optionen
• Unique Indexe– Nur eindeutige Werte erlaubt
• Sparse Indexe– Für Felder, die nicht in allen Dokumenten
vorkommen
• Geospatial Indexe– Zur Modellierung von Geoinformationen
• TTL Collections – Verfallen nach x Sekunden
// Der Name eines Rezepts muss eindeutig sein
> db.recipes.ensureIndex( { name: 1 }, { unique: true } )
// Erzwingen eines Index auf einer Collection mit nicht eindeutigen // Namen – Die Duplikate werden gelöscht
> db.recipes.ensureIndex(
{ name: 1 },
{ unique: true, dropDups: true }
)
* dropDups bitte mit sehr viel Vorsicht anwenden!
Unique Indexe
// Nur Dokumente mit dem Feld calories werden indexiert
> db.recipes.ensureIndex(
{ calories: -1 },
{ sparse: true }
)
// Kombination mit einem Unique Index möglich
> db.recipes.ensureIndex(
{ name: 1 , calories: -1 },
{ unique: true, sparse: true }
)
* Fehlende Felder werden im Index als null gespeichert
Sparse Indexe
// Hinzufügen von Längen- und Breitengraden
{
name: ‚codecentric Frankfurt’,
loc: [ 50.11678, 8.67206]
}
// Indexierung der Koordinaten
> db.locations.ensureIndex( { loc : '2d' } )
// Abfrage nach Orten in der Nähe von codecentric Frankfurt
> db.locations.find({
loc: { $near: [ 50.1, 8.7 ] }
})
Geospatial Indexe
// Die Dokumente müssen ein Datum des Typs BSON UTC haben
{ ' submitted_date ' : ISODate('2012-10-12T05:24:07.211Z'), … }
// Dokumente werden automatisch nach 'expireAfterSeconds' // Sekunden gelöscht
> db.recipes.ensureIndex(
{ submitted_date: 1 },
{ expireAfterSeconds: 3600 }
)
TTL Collections
Limitierungen von Indexen
• Collections können nicht mehr als 64 Indexe haben.
• Indexschlüssel können nicht größer als 1024 Byte sein.
• Der Name eines Index inklusive Namespace muss kleiner als 128 Zeichen sein.
• Abfragen können nur einen Index verwenden– Ausnahme: Abfragen mit $or
• Indexe verbrauchen Speichern und verlangsamen das Schreiben von Daten
Optimierung von Indexen
Vorgehensweise
1. Langsame Abfragen identifizieren
2. Mittels explain() mehr über die langsame Abfrage herausfinden
3. Anlegen der Indexe auf den abgefragten Feldern
4. Optimierung der Abfragen anhand der verwendeten Indexe
> db.setProfilingLevel( n , slowms=100ms )
n=0: Profiler abgeschaltet
n=1: Protokollieren aller Abfragen langsamer als slowms
n=2: Protokollieren aller Operationen
> db.system.profile.find()
* Die Collection profile ist eine Capped Collection und hat daher eine feste Anzahl von Einträgen
1. Langsame Abfragen identifizieren
> db.recipes.find( { calories:
{ $lt : 40 } }
).explain( )
{
"cursor" : "BasicCursor" ,
"n" : 42,
"nscannedObjects” : 53641
"nscanned" : 53641,
...
"millis" : 252,
...
}
* Keine Verwendung von Plänen aus dem Cache und erneuten Ausführungen
2. Benutzung von explain()
2. Metriken des Executionplans I
• Cursor– Der Typ des Cursors. BasicCursor bedeutet, dass
kein Index benutzt wurde
• n – Die Anzahl der passenden Dokumente
• nscannedObjects– Die Anzahl der gescannten Dokumente
• nscanned– Die Anzahl der untersuchten Einträge
(Indexeinträge oder Dokumente)
2. Metriken des Executionplans II
• millis– Ausführungszeit der Abfrage
• Komplette Referenz unter – http://docs.mongodb.org/manual/reference/explain
Das Verhältnis der gescannten zu den gefundenen Dokumenten sollte möglichst nahe an 1 sein!
3. Anlegen der Indexe auf den abgefragten Feldern
// Bei folgendem Index…
> db.collection.ensureIndex({ a:1, b:1 , c:1, d:1 })
// … können die folgenden Sortieroperationen und Abfragen den // Index benutzen
> db.collection.find( ).sort({ a:1 })
> db.collection.find( ).sort({ a:1, b:1 })
> db.collection.find({ a:4 }).sort({ a:1, b:1 })
> db.collection.find({ b:5 }).sort({ a:1, b:1 })
4. Optimierung der Abfragen anhand der verwendeten Indexe
// Bei folgendem Index…
> db.collection.ensureIndex({ a:1, b:1, c:1, d:1 })
// … können diese Abfragen ihn nicht verwenden
> db.collection.find( ).sort({ b: 1 })
> db.collection.find({ b: 5 }).sort({ b: 1 })
4. Optimierung der Abfragen anhand der verwendeten Indexe
// Bei folgendem Index…
> db.recipes.ensureIndex({ main_ingredient: 1, name: 1 })
// … verwendet diese Abfrage nur Felder des Index
> db.recipes.find(
{ main_ingredient: 'chicken’ },
{ _id: 0, name: 1 }
)
// Das Feld indexOnly bei explain() zeigt dies an
> db.recipes.find(
{ main_ingredient: 'chicken' },
{ _id: 0, name: 1 }
).explain()
{
"indexOnly": true,
}
4. Optimierung der Abfragen anhand der verwendeten Indexe
// MongoDB mitteilen, welcher Index verwendet werden soll
> db.recipes.find({
calories: { $lt: 1000 } }
).hint({ _id: 1 })
// Die Verwendung von Indexen ausschalten (z.B. zur Performance-// messung
> db.recipes.find(
{ calories: { $lt: 1000 } }
).hint({ $natural: 1 })
Index manuell angeben
Häufige Stolperfallen bei Indexen
// MongoDB kann nur einen Index pro Abfrage verwenden
> db.collection.ensureIndex({ a: 1 })
> db.collection.ensureIndex({ b: 1 })
// Nur einer der beiden obigen Indexe wird verwendet
> db.collection.find({ a: 3, b: 4 })
Mehrere Index verwenden
// Zusammengesetzte Indexe sind im Allgemeinen sehr effektiv
> db.collection.ensureIndex({ a: 1, b: 1, c: 1 })
// Aber nur wenn die Abfrage ein Präfix des Indexes ist…
// Diese Abfrage kann den Index nicht effektiv verwenden
db.collection.find({ c: 2 })
// …diese Abfrage hingegen schon
db.collection.find({ a: 3, b: 5 })
Zusammengesetzte Indexe
// Folgendes Feld hat nur sehr wenige eindeutige Werte
> db.collection.distinct('status’)
[ 'new', 'processed' ]
// Ein Index auf diesem Feld bringt nur sehr wenig
> db.collection.ensureIndex({ status: 1 })
> db.collection.find({ status: 'new' })
// Besser ist ein zusammengesetzter Index zusammen mit einem // anderen Feld
> db.collection.ensureIndex({ status: 1, created_at: -1 })
> db.collection.find(
{ status: 'new' }
).sort({ created_at: -1 })
Indexe mit geringer Selektivität
> db.users.ensureIndex({ username: 1 })
// Abfragen mit regulären Ausdrücken, die linksgebunden sind // können den Index verwenden
> db.users.find({ username: /^joe smith/ })
// Generische Abfragen mit regulären Ausdrücken hingegen nicht…
> db.users.find({username: /smith/ })
// Ebenso nicht schreibungsunabhängige Abfragen…
> db.users.find({ username: /Joe/i })
Reguläre Ausdrücke & Indexe
// Bei Negationen können Indexe nicht verwendet werden
> db.things.ensureIndex({ x: 1 })
// z.B. bei Abfragen mit not equal
> db.things.find({ x: { $ne: 3 } })
// …oder Abfragen mit not in
> db.things.find({ x: { $nin: [2, 3, 4 ] } })
// …oder Abfragen mit dem $not Operator
> db.people.find({ name: { $not: 'John Doe' } })
Negation
Konsistenz beim Schreiben und Lesen von Daten
Starke Konsistenz
Verzögerte Konsistenz
Write Concern - Schreibmodi
• Bestätigung durch das Netzwerk
• Bestätigung durch MongoDB
• Bestätigung durch das Journal
• Bestätigung durch Secondaries
• Bestätigung durch Tagging
Bestätigung durch das Netzwerk„Fire and forget“
Bestätigung durch MongoDB Wait for Error
Bestätigung durch das JournalWait for Journal Sync
Bestätigung durch SecondariesWait for Replication
Tagging beim Schreiben
• Verfügbar seit Version 2.0
• Ermöglicht stärkere Kontrolle woher Daten gelesen und wohin geschrieben werden
• Jeder Knoten kann mehrere Tags haben– tags: {dc: "ny"}– tags: {dc: "ny", subnet: „192.168", rack: „row3rk7"}
• Erlaubt das Anlegen für Regeln für das Write Concern pro Replikaset
• Anpassung der Regeln ohne Codeänderung
{
_id : "mySet",
members : [
{_id : 0, host : "A", tags : {"dc": "ny"}},
{_id : 1, host : "B", tags : {"dc": "ny"}},
{_id : 2, host : "C", tags : {"dc": "sf"}},
{_id : 3, host : "D", tags : {"dc": "sf"}},
{_id : 4, host : "E", tags : {"dc": "cloud"}}],
settings : {
getLastErrorModes : {
allDCs : {"dc" : 3},
someDCs : {"dc" : 2}} }
}
> db.blogs.insert({...})
> db.runCommand({getLastError : 1, w : "someDCs"})
Beispiel für Tagging
Bestätigung durch alle DatenzentrenWait for Replication (Tagging)
// Wait for network acknowledgement
> db.runCommand( { getLastError: 1, w: 0 } )
// Wait for error (Default)
> db.runCommand( { getLastError: 1, w: 1 } )
// Wait for journal sync
> db.runCommand( { getLastError: 1, w: 1, j: "true" } )
// Wait for replication
> db.runCommand( { getLastError: 1, w: “majority" } ) // Mehrheit
> db.runCommand( { getLastError: 1, w: 3 } ) // # der Secondaries
Setzen des Write Concerns
Modi zum Lesen von Daten (Seit Version 2.2)
• Nur Primary (primary)
• Primary bevorzugt (primaryPreferred)
• Nur Secondaries (secondary)
• Secondaries bevorzugt (secondaryPreferred)
• Nähester Knoten (Nearest)
Falls mehr als ein Knoten möglich ist, wird immer der näheste Knoten zum Lesen der Daten verwendet. (Alle Modi außer Primary)
Nur Primaryprimary
Read
Primary bevorzugtprimaryPreferred
Read
Read
Nur Secondariessecondary
Read
Read
Secondaries bevorzugtsecondaryPreferred
Read
Read
Read
Nähester Knotennearest
Read
Read
Read
Tagging beim Lesen
• Ermöglicht eine individuelle Kontrolle woher Daten gelesen werden– z.B. { "disk": "ssd", "use": "reporting" }
• Lässt sich mit den Standard-Lese-Modi kombinieren– Außer dem Modus „Nur Primary“
// Nur Primary
> cursor.setReadPref( “primary" )
// Primary bevorzugt
> cursor.setReadPref( “primaryPreferred" )
….
// Nur Secondaries mit Tagging
> cursor.setReadPref( “secondary“, [ rack : 2 ] )
Setzen der Read Preference
Aufruf der Methode auf dem Cursor muss vor dem Lesen der Dokumente erfolgen
Java API & Frameworks
Übersicht
Hibernate OGM
Spring Data MongoDB
MongoDB Java Driver
Morphia
JPA
JDBC
MongoDB
Jongo
MongoDB Java Treiber
MongoDB Treiber
• Ein Wire Protokoll für alle Programmiersprachen
• Eine Implementierung des Treibers pro Sprache
• Hauptaufgaben:– Konvertierung der sprachspezifischen
Datenstrukturen nach BSON– Generierung der ObjectId für das Feld _id
MongoDB Java Treiber
• Ein JAR ohne weitere Abhängigkeiten:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.11.3</version>
</dependency>
Verfügbar auf Github:https://github.com/mongodb/mongo-java-driver
Verbindungsaufbauimport com.mongodb.MongoClient;
// Default: localhost:27017
mongo = new MongoClient();
// Replica set
mongo = new MongoClient(Arrays.asList(
new ServerAddress("replicant01", 10001),
new ServerAddress("replicant02", 10002),
new ServerAddress("replicant03", 10003)
));
// Sharding: mongos server
mongo = new MongoClient("mongos01", 4711);
Zugriff auf Datenbank und Collection
import com.mongodb.DB;
import com.mongodb.DBCollection;
DB db = mongo.getDB("test");
DBCollection collection = db.getCollection("foo");
Dokument einfügen
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
// insert document
DBObject doc = new BasicDBObject();
doc.put("date", new Date());
doc.put("i", 42);
collection.insert(doc);
Dokumente abfragen
import com.mongodb.DBCursor;
DBCursor cursor;
cursor = collection.find(); // all documents
// documents w/ {i: 42}
cursor = collection.find( new BasicDBObject("i", 42) );
document = cursor.next(); ...
> db.order.find( {"items.quantity": ? } )
Beispiel: Bestellung
Beispiel: Bestellung IDB db = mongo.getDB("test");DBCollection collection = db.getCollection("order");DBObject order;List<DBObject> items = new ArrayList<DBObject>();DBObject item;
// orderorder = new BasicDBObject();order.put("date", new Date());order.put("custInfo" , „Sheldon Cooper");// itemsitem = new BasicDBObject();item.put("quantity", 1);item.put("price", 47.11);item.put("desc", "Item #1");items.add(item);item = new BasicDBObject();item.put("quantity", 2);item.put("price", 42.0);item.put("desc", "Item #2");items.add(item);order.put("items", items);
collection.insert(order);
Beispiel: Bestellung II
DB db = mongo.getDB("test");DBCollection collection = db.getCollection("order");DBObject query;DBObject document;DBCursor cursor;
query = new BasicDBObject("items.quantity", 2);cursor = collection.find(query);
while ( cursor.hasNext() ) {document = cursor.next();println(document);
}
Jongo
Kurzüberblick über Jongo
Entwickler Benoît Guérout, Yves AmsellemLizenz Apache License, Version 2.0Dokumentation http://jongo.org/Hauptmerkmale • Object/Document Mapping
• Eigene Query API
Query in Java as in Mongo Shell
Jongo: Object Mapping
public class Order {private ObjectId id;private Date date;@JsonProperty("custInfo") private String customerInfo;List<Item> items; …
}
public class Item {private int quantity;private double price;@JsonProperty("desc") private String description;…
}
Jongo: Abfragen
// Java driver APIMongoClient mc = new MongoClient();DB db = mc.getDB("odm_jongo");// Jongo API entry pointJongo jongo = new Jongo(db);MongoCollection orders = jongo.getCollection("order");
// no DAO neededIterable<Order> result =
orders.find("{\"items.quantity\": #}", 2).as(Order.class);// supports projectionIterable<X> result =
orders.find().fields("{_id:0, date:1, custInfo:1}").as(X.class);
Morphia
Kurzüberblick über Morphia
Entwickler Scott Hernandez, James GreenLizenz Apache License, Version 2.0Dokumentation https://github.com/jmkgreen/morph
ia/wiki/OverviewHauptmerkmale • Object/Document Mapping
• Eigene Query API• Unterstützung für DAO‘s
Morphia: Object Mapping
public class Order {@Id private ObjectId id;private Date date;@Property("custInfo") private String customerInfo;@Embedded List<Item> items;...
}public class Item {
private int quantity;private double price;@Property("desc") private String description;...
}
Morphia: Abfragen
public class OrderDao extends BasicDAO<Order, ObjectId> {
List<Order> findByItemsQuantity(int quantity) {returnfind( createQuery().filter("items.quantity", quantity)).asList();
}List<Order> findByItemsPriceGreaterThan(double price) {
returnfind(
createQuery().field("items.price").greaterThan(price) ).asList();
}…
}
Morphia: Eigene Syntax für Abfragen
Morphia Mongo Query= $eq!=, <> $neq>, <, >=,<= $gt, $lt, $gte, $ltein, nin $in, $ninelem $elemMatch… ….
Spring Data MongoDB
Kurzüberblick über Spring Data MongoDB
Hersteller VMware / SpringSourceLizenz Apache License, Version 2.0Dokumentation http://www.springsource.org/spring
-data/mongodbHauptmerkmale • Repository Support
• Object/Document Mapping• Templating
Einheitliche Architektur für relationale Datenbanken sowie für unterstützte NoSQL-Stores
Spring Data
RDBMS MongoDB Neo4j …
Spring Data JPA
CrudRepository PagingAndSortingRepository
JpaRepository
JPA
JDBC
Spring Data MongoDB
MongoRepository
MongoTemplate
Spring DataNeo4j
Spring Data…
GraphRepository
Neo4jTemplate
Mongo Java Driver
Embedded REST
Spring Data MongoDB
• Repository Support– Abfragen werden aus den Methodensignaturen erstellt– Annotationen für Abfragen
• Object-Document-Mapping– Annotationen: @Document, @Field, @Index, …– Klassen werden auf Collections gemappt, Javaobjekte auf
Dokumente
• Templating– Abstraktion der Ressourcen– Konfigurierbarkeit der Verbindungen zu MongoDB– Abdeckung des Lebenszyklus einer Collection– Unterstützung für Map/Reduce & Aggregation Framework
Spring Data MongoDB:Konfiguration<!-- Connection to MongoDB server -->
<mongo:db-factory host="localhost" port="27017" dbname="test" />
<!-- MongoDB Template -->
<bean id="mongoTemplate,class="org.springframework.data.mongodb.core.MongoTemplate"><constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
<!-- Package w/ automagic repositories -->
<mongo:repositories base-package="mongodb" />
Spring Data MongoDB: Template<mongo:mongo host="${mongo.host}" port="${mongo.port}">
<mongo:optionsconnections-per-host="${mongo.connectionsPerHost}„threads-allowed-to-block-for-connectionmultiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}„connect-timeout="${mongo.connectTimeout}„max-wait-time="${mongo.maxWaitTime}„auto-connect-retry="${mongo.autoConnectRetry}„socket-keep-alive="${mongo.socketKeepAlive}„socket-timeout="${mongo.socketTimeout}„slave-ok="${mongo.slaveOk}„write-number="1„write-timeout="0„write-fsync="true"/>
</mongo:mongo><mongo:db-factory dbname= "test" mongo-ref="mongo"/>
Spring Data MongoDB: Object Mappingpublic class Order {
@Id private String id;private Date date;@Field("custInfo") private String customerInfo;List<Item> items; ...
}
public class Item {private int quantity;private double price;@Field("desc") private String description;...
}
Spring Data MongoDB: Repository Support
public interface OrderRepository extendsMongoRepository<Order, String> {
List<Order> findByItemsQuantity(intquantity);
List<Order> findByItemsPriceGreaterThan(doubleprice);
}
Spring Data MongoDB: Zusätzliche Unterstützung für…
• Map/Reduce & das Aggregation Framework
• Das Management von Indexen
• Große Dateien mittels GridFS
• Geoinformatische Indexe und Abfragen
• Optimistisches Locking
Hibernate OGM
Kurzüberblick über Hibernate OGM MongoDB
Hersteller JBoss / RedhatLizenz GNU LGPL, Version 2.1Dokumentation http://www.hibernate.org/subproject
s/ogm.htmlHauptmerkmale • JPA API (Teilweise)
• JPQL Query Language
Hibernate OGM
• Implementiert ein Subset der JPA API
• JP-QL Anfragen werden in native Datenbankabfragen übersetzt
• Unterstützt Infinispan, EhCache, MongoDB
Architektur vonHibernate OGM
Source:
http://docs.jboss.org/hibernate/ogm/4.0/reference/en-US/html/ogm-architecture.html#d0e409
Hibernate OGM MongoDB: Konfiguration
<persistence version="2.0" …><persistence-unit name="primary">
<provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider><class>hibernate.Order</class><class>hibernate.Item</class><properties><property name="hibernate.ogm.datastore.provider"value="org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastorePr
ovider"/><property name="hibernate.ogm.mongodb.database" value=„odm"/><property name="hibernate.ogm.mongodb.host" value=„localhost"/><property name="hibernate.ogm.mongodb.port" value=„27017"/>
</properties></persistence-unit>
</persistence>
@Entity@NamedQuery(
name="byItemsQuantity", query = "SELECT o FROM Order o JOIN o.items i WHERE i.quantity = :quantity")
public class Order {@GeneratedValue(generator = "uuid")@GenericGenerator(name = "uuid", strategy = "uuid2")@Id private String id;
private Date date;
@Column(name = "custInfo") private String customerInfo;
@ElementCollectionprivate List<Item> items;
@Embeddablepublic class Item {
private int quantity;
private double price;
@Column(name="desc") private String description; ...
Hibernate OGM MongoDB: Object Mapping
Hibernate OGM MongoDB: Aktueller Status
• Frühes Beta-Stadium
• Aktuell nur Speichern / Mergen / Löschen von Daten möglich
• Noch keine Unterstützung für Abfragen
• Benutzt eine relationale API
Not yet ready for prime time…
Empfehlung
Was sollte ich einsetzen?
Hibernate OGM
Spring Data MongoDB
MongoDB Java Driver
Morphia
JPA
JDBC
MongoDB
Jongo
- „Ready for production“- Von MongoDB Inc. unterstützt
- Ältestes Framework- Nicht so mächtig wie Spring
- „Ready for production“- Aktive Community
- Frühes Betastadium- Diskrepanz in API
Der „bessere“ Treiber
Getting started…
One more thing…
MongoDB User Group
https://www.xing.com/net/mongodb-ffm/
http://www.meetup.com/Frankfurt-Rhine-Main-MongoDB-User-Group/
MongoDB Munich 2013
http://www.mongodb.com/events/mongodb-munich-2013/
So Long, and Thanks for All the Fish!