NoSQL - Die Onleihe

Werbung
stefan EDLICH
achim FRIEDLAND
jens HAMPE
benjamin BRAUER
N SQL
EINSTIEG IN DIE WELT
NICHTRELATIONALER
WEB 2.0 DATENBANKEN
EMPFOHLEN VON
2
2 NoSQL – Theoretische Grundlagen
Wenn Sie NoSQL-Datenbanken einsetzen wollen, sollten Sie auch mit den wichtigsten
Grundbegriffen und theoretischen Ansätzen auskennen, auf denen diese Systeme aufbauen.
Einige davon sind quasi traditionelle Verfahren der klassischen Datenbankwelt und Ihnen
vielleicht schon bekannt. Daneben gibt es aber in der NoSQL-Welt auch Algorithmen und
Protokolle, die bisher noch nicht häufig verwendet wurden. Diese wollen wir in diesem
Kapitel vorstellen. Dazu zählen:
Map/Reduce
CAP-Theorem/Eventually Consistent
Consistent Hashing
MVCC-Protokoll
Vector Clocks
Paxos
Mit diesen Grundlagen sind Sie gut gerüstet, wenn Sie sich die NoSQL-Welt erobern wollen, weil die meisten NoSQL-Datenbanken auf diesem minimalen Fundament aufbauen.
Eine vollständige Darstellung der Theorie, die den NoSQL-Systemen zugrunde liegt, würde eigentlich ein eigenes Buch erfordern. Im Rahmen unseres Buches können und wollen
wir nur die wichtigsten Themen ansprechen.
Wenn Sie noch tiefer in die Thematik einsteigen wollen, als wir es in dieser Einführung
tun, müssten Sie sich auch noch mit folgenden Themen auseinandersetzen:
B- und B*-Trees sowie beispielsweise Quad-Trees, die für Key/Value-Datenbanken,
räumliche Indexierung und Kollisionserkennung wichtig sind.
Transaktionsprotokolle und Consensus-Protokolle; Protokolle wie 2PC, 3PC sind für
die klassische Datenbankwelt und auch für NoSQL wichtige Grundlagen. Diese sind
aber in der Literatur sehr gut behandelt.
Die Themen Replikation, Partitionierung und Fragmentierung der Daten spielen für
NoSQL ebenfalls eine wichtige Rolle. Zu diesem Bereich gehören evtl. auch verschiedene Quorum-Strategien.
11
2 NoSQL – Theoretische Grundlagen
Literatur zu allen obigen Themen finden Sie entweder in Büchern zu Algorithmen oder in
umfassenderen Standardwerken zum Thema Datenbanken:
Gunter Saake, Kai-Uwe Sattler, Algorithmen und Datenstrukturen: Eine Einführung
mit Java, dpunkt Verlag, 2010
Hector Garcia-Molina, Jeffrey D. Ullman und Jennifer Widom, Database Systems. The
Complete Book, Pearson International, 2nd Edition, 2008
2.1
Map/Reduce
Um die rasant wachsende Menge von Daten und Informationen effizient verarbeiten zu
können, wurden neue alternative Algorithmen, Frameworks und Datenbankmanagementsysteme entwickelt. Bei der Verarbeitung großer Datenmengen in der Größenordnung von
vielen Terabytes bis hin zu mehreren Petabytes spielt das in diesem Kapitel beschriebene
Map/Reduce-Verfahren eine entscheidende Rolle. Mittels eines Map/Reduce-Frameworks
wird eine effiziente nebenläufige Berechnung über solch große Datenmengen in Computerclustern erst ermöglicht.
Entwickelt wurde das Map/Reduce-Framework 2004 bei Google Inc. von den Entwicklern
Jeffrey Dean und Sanjay Ghemawat. Eine erste Vorstellung und Demonstration, beschrieben in [Dean04], erfolgte auf der Konferenz „OSDI 04, Sixth Symposium on Operating
System Design and Implementation“ in San Francisco, Kalifornien, im Dezember 20041.
Im Januar 2010 hat Google Inc. auf das dort vorgestellte Map/Reduce-Verfahren vom USamerikanischen Patentbüro ein Patent erhalten2. Experten sind sich allerdings einig, dass es
sich hierbei um ein Schutzpatent für Google selbst handelt und Google nun keine Klagewelle beginnen wird.
Die grundlegende Idee, Komponenten, Architektur und Anwendungsbereiche des Map/Reduce-Verfahrens werden in diesem Abschnitt 2.1 vorgestellt und beschrieben. Die zahlreichen Implementierungen, die seit der ersten Vorstellung dieses Verfahrens entwickelt
wurden, werden zur Übersicht kurz aufgelistet. Abgerundet wird dieser Abschnitt durch
ein einfaches Einsatzbeispiel, welches zum praktischen Nachvollziehen des beschriebenen
Map/Reduce-Algorithmus ermuntern soll.
2.1.1
Funktionale Ursprünge
Die Parallelisierung von Prozessen beginnt bei der Formulierung von Algorithmen. Parallelisierung ist eine Stärke der funktionalen Sprachen. Die Grundidee von Map/Reduce
kommt daher auch von funktionalen Programmiersprachen wie LISP3 und ML4. Hinsicht1
http://labs.google.com/papers/mapreduce.html, 28.02.2010
Patent Nummer: US007650331; zu finden bei http://patft.uspto.gov
3 List Processing
4 Meta Language
2
12
2.1 Map/Reduce
lich der Parallelisierung bieten funktionale Sprachen aufgrund ihrer Arbeitsweise Vorteile
gegenüber anderen Sprachen. Es entstehen keine Seiteneffekte wie Verklemmungen (deadlock) und Wettlaufsituationen (race conditions). Funktionale Operationen ändern die vorhandenen Datenstrukturen nicht, sie arbeiten immer auf neu erstellten Kopien vorhandener
Daten. Die Originaldaten bleiben unverändert erhalten. Unterschiedliche Operationen auf
dem gleichen Datensatz beeinflussen sich somit nicht gegenseitig, da jede Operation auf
einer eigenen Kopie der Originaldaten angewendet wird oder bei Datenergänzungen eine
neue Datenstruktur erzeugt wird. Ohne Seiteneffekte spielt auch die Ausführungsreihenfolge von Operationen keine Rolle, wodurch die Parallelisierung dieser Operationen möglich wird.
Das Konzept einer Funktion im Sinne der Mathematik ist in der funktionalen Programmierung am klarsten umgesetzt. Hier stellen die Funktionen Abbildungsvorschriften dar. Eine
Funktion besteht dann aus einer Reihe von Definitionen, die diese Vorschrift beschreibt.
Ein funktionales Programm besteht ausschließlich aus Funktionsdefinitionen und besitzt
keine Kontrollstrukturen wie Schleifen. Wichtigstes Hilfsmittel für die funktionale Programmierung ist daher die Rekursion. Funktionen sind in funktionalen Programmiersprachen Objekte, mit denen wie mit Variablen gearbeitet werden kann. Insbesondere können
Funktionen als Argument oder Rückgabewert einer anderen Funktion auftreten. Man spricht
dann von Funktionen höherer Ordnung.
Die aus der funktionalen Programmierung bekannten Routinen map() und fold(), auch als
reduce() bezeichnet, werden in modifizierter Form im Map/Reduce-Algorithmus jeweils
nebenläufig in zwei Phasen ausgeführt. Sie zählen zu den Funktionen höherer Ordnung.
Wie der Name der ältesten funktionalen Programmiersprache LISP (= List Processing)
schon verrät, geht es dabei um die Verarbeitung von Listen. Die Funktion map() wendet
eine Funktion sukzessive auf alle Elemente einer Liste an und gibt eine durch die Funktion
modifizierte Liste zurück. Die Funktion reduce() akkumuliert einzelne Funktionsergebnisse der Listenpaare und reduziert sie damit auf einen Ausgabewert. Diese beiden Funktionen werden in modifizierter Ausprägung als Map/Reduce-Algorithmus jeweils parallel auf
verschiedenen Knoten im Netzwerk in zwei Phasen hintereinander angewendet. Das Besondere an der Map/Reduce-Formulierung ist, dass sich mit den zwei Phasen jeweils eine
Parallelisierungsmöglichkeit ergibt, die innerhalb eines Computer-Clusters für eine beschleunigte Berechnung von sehr großen Datenmengen verwendet werden kann. Bei solchen Datenmengen ist eine Parallelisierung unter Umständen allein schon deshalb erforderlich, weil diese Datenmengen für einen einzelnen Prozess und das ausführende Rechnersystem bereits zu groß sind.
Die map()-Funktion der funktionalen Sprachen erhält als Argument eine Funktion f und
wendet diese auf jedes Element einer übergebenen Liste an. Es ist eine polymorphe Funktion, die beliebige Argumenttypen erhalten kann, wie durch die Typvariablen a und b in
Listing 2.1.1, angegeben ist.
13
2 NoSQL – Theoretische Grundlagen
Listing 2.1.1 Haskell-Definition der map-Funktion5
map :: (a -> b) -> [a] -> [b]
map f []
= []
map f (x : xs)
= f x : map f xs
Die erste Zeile in der Funktionsdefinition wird als Typsignatur bezeichnet und beschreibt
die Aufgabe der Funktion. Sie wendet die Funktion (a->b) auf eine Liste [a] an und gibt
die Liste [b] zurück. Die zweite und dritte Zeile definieren das Verhalten der map()Funktion für verschiede Eingabemuster. Bei Eingabe einer Funktion f und einer leeren
Liste [] wird als Ergebnis auch nur eine leere Liste [] zurückgegeben. Die dritte Zeile
zeigt, dass bei Eingabe einer Funktion f und einer Liste, die durch die Listenkonstruktion(x:xs)dargestellt wird, die Funktion f auf das erste Listenelement x und anschließend
dann rekursiv auf die restliche Liste xs angewendet wird.
Ein Funktionsaufruf in Haskell mit folgenden Angaben:
map (\x -> x^2) [1,2,3,4,5]
ergibt das Ergebnis, wie auch in Abbildung 2.1.1 dargestellt:
[1,4,9,16,25]
Listenelemente der Eingabe
1
2
3
4
5
f
f
f
f
f
1
4
9
16
25
Listenelemente der Ausgabe
Abbildung 2.1.1
Anwenden der map()-Funktion
Die map()-Funktion -lässt sich nach Definition auf beliebige Datentypen anwenden. Das
folgende Beispiel wendet die Funktion toUpper auf jedes Zeichen des Strings „nosql“
an. Um die Funktion toUpper nutzen zu können, muss sie vorher durch den Befehl
:module +Data.Char in den Arbeitsbereich importiert werden:
:module +Data.Char
map toUpper "nosql"
und erzeugt damit die Zeichenausgabe in Großbuchstaben (siehe auch Abbildung 2.1.2):
"NOSQL"
Die Reihenfolge der Elementeingabe und Elementausgabe bleibt bei dieser n-zu-n-Transformation erhalten, wobei n = Anzahl der Listenelemente ist.
5
14
Vgl. [Rech02] Seite 559
2.1 Map/Reduce
Listenelemente der Eingabe
n
o
s
q
l
f
f
f
f
f
N
O
S
Q
L
Listenelemente der Ausgabe
Abbildung 2.1.2
Anwendung der map()-Funktion
mit toUpper
Die fold()-Funktion realisiert quasi eine n-zu-1-Transformation und wird in diesem Zusammenhang auch in anderen Sprachen als reduce-, accumulate-, compress- oder injectFunktion bezeichnet. Das Ergebnis dieser Transformation muss nicht aus einem Element
bestehen, es kann auch wiederum eine Liste von reduzierten Elementen sein. In Haskell
wie auch in vielen anderen funktionalen Programmiersprachen werden zwei Varianten von
fold() unterschieden: die foldl-Funktion für die Bearbeitung einer Liste von links nach
rechts und die foldr-Funktion für die Bearbeitung einer Liste von rechts nach links6.
Listing 2.1.2 Haskell-Definitionen für foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f z []
= z
foldl f z (x:xs) = foldl f (f z x) xs
Listing 2.1.3 Haskell-Definitionen für foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z []
= z
foldr f z (x:xs) = f x (foldr f z xs)
Die Typsignaturen der Funktionen in den jeweils ersten Zeilen geben die Aufgaben der
Funktionen wieder. Die Argumente der foldl-Funktion bestehen aus einer Schrittfunktion
(a -> b -> a), einem initialen Wert für einen Akkumulator a und einer Eingabeliste [b].
Die Schrittfunktion (a -> b -> a)wendet den Akkumulator a auf ein Listenelement b an
und gibt einen neuen Akkumulator a zurück. Die Schrittfunktion wird nun rekursiv für alle
Listenelemente angewendet und liefert den akkumulierten Rückgabewert a. Das Verhalten
für verschiedene Eingabemuster wird in den jeweils folgenden beiden Zeilen des Listings
definiert. Bei Eingabe einer Schrittfunktion f, einem Akkumulatorwert z und einer leeren
Liste [] besteht das Ergebnis auch nur aus dem initialen Wert des Akkumulators z. Bei
Eingabe einer Schrittfunktion f, einem Akkumulatorwert z und einer nicht leeren Liste
(x:xs) wird foldl f rekursiv auf alle Listenelemente xs angewendet, wobei der jeweils
neue Anfangswert das Ergebnis der Zusammenlegung des alten ursprünglichen Wertes z
mit dem nächsten Element x ist, ausgedrückt durch (f z x).
6
Vgl. [Rech02] Seite 559-560
15
Register
.
.NET-4.0 207
10
10gen 101, 116
A
Abfrage 277
Acceptors 48
ACID 30, 184, 276
ACM 31
Adjazenzliste 176
Adjazenzmatrix 174
Adobe 55
Amazon 1, 34
Amazon Dynamo 157, 259
Amazon EC2 145
Amazon Elastic MapReduce 24
Amazon Web Services 83
Apache Hadoop 55
Apache Hama 248
Apache Jackrabbit 259
Apache Thrift 272
App Engine 268
Armstrong, Joe 103
Atom 223
Ausfalltoleranz 31
Availability 31
AWS 83
B
Backup 280
Backup/Restore 280
baidu.com 256
ballots 48
BASE 33, 156, 276
BashReduce 24
BBC 114
BerkeleyDB 1, 228, 267
BerkeleyDB XML 270
BigTable 1, 54, 69, 268
Breitensuche 176
BSON 117
B-Tree 266
Bucket 156
Bulk-Kommando 137
Business-Daten 273
C
C# 204
Canonical 114
CAP-Theorem 31
Cassandra 69
Causal Consistency 34
Cell-Prozessorserie 24
Chord 39
Chordless 146
Chubby-Prozess 50
Chunk 126
Client-Sharding 144
Cloud-Computing 85
285
Register
Clustering 158
Codd 180
Column Families 6
Consistency 31
Consistent-Hashing 36, 260
CouchApps 113
CouchDB 24, 102
CouchDB-Lounge 113, 260
Couchio 106
Crash Resistance 281
C-Store 53, 270
cURL 108
D
Data Access Pattern 279
Data-Structure Server 144
Data-Warehouse 53
Daten- und Speichermodell 274
Datenart 273
Datenkomplexität 275
Datenmenge 275
Datennavigation 275
Datenzugriffsmuster 279
db4o 1, 270
DBObject 120
DEX 215
Dictionary 144
Disco 24
Django 257
Document Stores 6
Domain-Daten 273
Dynomite 260
E
EAV-Model 181
EMC Documentum 270
Emit 19
Engine Yard 114
Erlang/OPT 103
ETS 265
Euler, Leonhard 172
Eulerkreise 177
Evans, Eric 1
286
Event-Daten 273
eXist 270
F
Facebook 1
Filament 251
FileMap 23
Filtering 192
flaps 243
FlockDB 242, 268
FluidDB 53
fold 13
funktionale Sprache 13
Futon 107, 111
G
Gemstone 270
geographische Daten 273
GFS 1, 20
GigaSpaces 7, 269
Gizzard 268
Go 24
Google 12, 50, 268
Google Pregel 245
Google Query Language 268
GQL 196, 200, 268
Graph Query Language 196
Graphdatenbank 6
Graphtraversierungen 176
Greenplum 23, 270
Gremlin 190
Grep 22
Grid-Datenbank 6
GridFS 117
GridGain 23
GROUP BY 124
GT.M 1, 268
GXEF 207
H
Hamiltonkreis 177
Hamiltonweg 177
Hashes 140
Register
Hashfunktionen 36
Haskell 14
Hazelcast 7, 269
HBase 54
HDFS 68, 256
Hidden-Handoff 157
Hinted Handoff 165
Hive 272
Holumbus 24
HQL 256
HyperGraphDB 222
Hyperkante 174
HyperSQL 151
Hypertable 256
I
IBM 103
IBMs Lotus 269
InfiniteGraph 229
InfoGrid 1, 209
Inzidenzmatrix 175
IP-Adresse 44
ISIS 270
J
Jabber 227
Java 161
JavaScript 104, 116, 163
JDBC 141
JDO 268
join 163, 171
JPA 268
JRuby 58, 187
JSON 8, 62, 103, 156, 182
Judd, Doug 256
JVM 195
K
KAI 261
Key/Hash-Datenbank 1
keyspace 50
Keyspaces 54
Key/Value 6
Konsistenz 30
Kosten 85
Kryptographie 36
Kyoto Cabinet 266
L
Leader 48
Link 157
LinkedIn 1
link-walking 163
LINQ 4
LISP 13
Liste 138
Location Based Services 8
Log-Daten 273
Lotus Notes 1
Lucene 185
M
MAC-ID 44
map 13
Map/Reduce 1, 12, 123
MarkLogic 270
Master 20
MEMBASE 261
Memcached 1
MemcacheDB 267
Message-Daten 273
Metadaten 273
MonetDB 53, 270
MongoDB 24, 115
Monotonic Read Consistency 35
Monotonic Write Consistency 35
Multi-Node Cluster 58
Multiprozessor 144
MVCC 41, 256
MySpace 1
MySql 31
MySQL 243
N
Neo4j 1, 8, 184
NVIDIA GPUs 24
287
Register
O
Objectivity 229, 270
Objectivity/DB 229
Objektdatenbank 6, 270
objektorientiertes Datenmodell 181
OLAP 53
Open Source 3
OpenSSL 105
OrientDB 236, 258
Oskarsson, Johan 1
P
PaaS 268
Paging 139
Partition Tolerance 31
Patent 12
Pattern-Matching 166
Paxos 48, 265
Peer-to-Peer 155
Performance-Dimensionen 277
Phoenix 23
Pig 272
Pipelining 144
Postgres 31
Prevayler 269
Primärschlüssel 56
Progress 270
Property-Graph 198, 230
Proposer 48
protobuf 62
Prüfsummen 36
Pruning 191
Q
Qizmt 24
Qt-Concurrent 24
Quorum 166
Quorum-Consensus-Algorithmen 47
R
RAM 132
RavenDB 259
RDF 8
288
RDF-Schema 182
Read-your-write Consistency 34
rediff.com 256
Redis 132
redis-conf 143
Refactoring 279
RegionServer 66
Replikation 127, 157, 279
REST 4, 156, 188
Riak 39, 101, 156
Ring 38, 154, 164
Ring-Adressraum 156
Ruby 141, 162
rufus-tokyo 266
S
SaaS 97, 268
Sanfillippo, Salvatore 132
Scala 142
Scalaris 50, 264
Scale-out 277
Scale-up 277
Scalien 50
Schema 116
Schlüsselraum 164
SciDB 54
Scratchpad 89
Sector/Sphere 24
Sedna 270
Semantic Web 181
Session Consistency 34
Session-Daten 273
Sets 138
Shard 126
Sharding 125, 194
shared memory 19
shared-nothing 157
Shell 221
Sibling 160
Sicherheit 280
Single-Node Cluster 58
Skalierbarkeit 3
Skynet 24
Register
SNMP-Protokoll 158
SOAP 84
Solr 185
sones 196
SonesDB 8
Spaltenfamilie 56
SPARQL 189
Sprache, funktionale 13
SQL 166
Stargate 61
Strozzi, Carlo 1, 5
StumbleUpon 55
Super Columns 72, 78
SwapSpace 265
Sybase IQ 53, 270
T
Tamino 270
telnet 135
temporäre Daten 273
Terrastore 258
Thompson, Ken 1
Thrift 63, 256
ThruDB 259
Tiefensuche 176
Time To Live 137
Timeout 164
Tinkerpop 189
Tokyo Cabinet 265, 266
Tokyo Dystopia 266
Tokyo Promenade 266
Tokyo Tyrant 266
Traveling Salesman 177
Traverser 191
Tupel 117
Tuple Stores 6
Twister 23
Twitter 243, 268
two-phase commit 184
Two-Phase-Commit-Protokoll 47
U
Ubuntu 114, 159
UNIX 133
UTF-8 141
UUID 187
V
Vector Clock 43, 158, 160, 260
Verfügbarkeit 31
Versant 270
Versions-Vektoren 44
VertexDBs 248
Vertica 270
Views 104, 111
virtueller Server 39
VMWare 132
Vogels, Werner 34
vNodes 165
W
Web Ontology 182
WebShell 200
Wide Column Stores 6
Worker 20
X
X.509 88
Xindice 270
XLINK 182
XML-Datenbank 6, 270
XMPP 227
XPRISO 210
Y
Yahoo 1, 55
YAML 8
Z
zookeeper 57
289
Herunterladen