MongoDB für Java-Programmierer

Preview:

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 : “sheldon@bigbang.com“ }

)

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" : "sheldon@bigbang.com"

}

// Alle Dokumente einer Collection anzeigen> db.user.find(){

"_id" : ObjectId("516684a32f391f3c2fcb80ed"),"name" : "Sheldon","mail" : "sheldon@bigbang.com"

}

Lesen aus einer Collection

// Filtern von bestimmten Dokumenten> db.user.find( { name : ”Penny” } ){

"_id" : ObjectId("5166a9dc2f391f3c2fcb80f1"),"name" : "Penny","mail" : "penny@bigbang.com"

}

// Nur bestimmte Felder anzeigen> db.user.find( { name : ”Penny” },

{_id: 0, mail : 1} )

{ "mail" : "sheldon@bigbang.com" }

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 : amy@bigbang.com }

] } )

// Und-Verknüpfung> db.user.find(

{$and : [ { name : “Sheldon“ }, { mail : amy@bigbang.com }

] } )

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 : “sheldon@howimetyourmother.com“ } )

// Anzeige des Updatesdb.user.findOne(){

"_id" : ObjectId("516684a32f391f3c2fcb80ed"),"mail" : "sheldon@howimetyourmother.com"

}

Update von Dokumenten I

Aufpassen beim Update von Dokumenten!

// Löschen des Dokuments> db.user.remove(

{ mail : “sheldon@howimetyourmother.com“ } )

// 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 : “sheldon@howimetyourmother.com“

} } )

// Anzeige des Updatesdb.user.find(name : “Sheldon“){

"_id" : ObjectId("5166ba122f391f3c2fcb80f5"),"mail" : "sheldon@howimetyourmother.com","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" : "sheldon@bigbang.com","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!

Recommended