Norman Soetbeer: Konzeption und Implementierung einer

Werbung
Konzeption und Implementierung einer Sichtenverwaltung für
NoSQL–Datenbanken
Diplomarbeit
Universität Rostock
Fakultät für Informatik und Elektrotechnik
Institut für Informatik
vorgelegt von:
Matrikelnummer:
geboren am:
Erstgutachter:
Zweitgutachter:
Betreuer:
Abgabedatum:
Norman Soetbeer
6201050
18.01.1985 in Schwerin
PD Dr. -Ing. habil. Meike Klettke
Prof. Dr.-Ing. Thomas Kirste
PD Dr. -Ing. habil. Meike Klettke
1. Oktober 2014
Zusammenfassung
NoSQL-Datenbanken sind in der Regel als schemalos oder schemaflexibel anzusehen. Mit
dieser Flexibilität wächst jedoch auch der Aufwand, heterogene Daten innerhalb von Anwendungen zu verarbeiten.
In dieser Diplomarbeit wird untersucht, inwiefern Sichtenkonzepte aus der SQL-Welt auf
NoSQL-Datenbanken übertragen und genutzt werden können, um diese Heterogenitäten zu
beseitigen. Hierzu werden Sichtendefinition für NoSQL-Systeme mit Hilfe von Beispielen beschrieben. Anschließend wird ein Konzept für die Sichtenverwaltung und -anfrage entwickelt
und in Form eines Prototyps für die Dokumentdatenbank MongoDB umgesetzt. Die Implementierung für eine Middleware auf Node.js-Basis wird schrittweise beschrieben Es erfolgt eine Evaluierung dieses Sichtenkonzepts hinsichtlich der praktischen Nutzung sowie der
Übertragbarkeit auf andere NoSQL-Datenbanken.
Schlüsselwörter
NoSQL, MongoDB, Sichtenverwaltung, Sichtdefinition
3
Inhaltsverzeichnis
1 Einleitung
9
2 Begleitendes Beispiel
2.1 Beispielanwendung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Schema-Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 Verwendete Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
11
12
12
3 State of the Art
3.1 Sichten in SQL . . . . . . . . . . . . .
3.1.1 Aufgaben von Sichten in SQL .
3.1.2 Arten von Sichten . . . . . . .
3.2 NoSQL-Datenbanken . . . . . . . . . .
3.2.1 Dokumentdatenbanken . . . . .
3.2.2 Spaltenorientierte Datenbanken
3.3 NoSQL-Anfragesprachen . . . . . . . .
3.3.1 Map-Reduce . . . . . . . . . .
3.4 MongoDB . . . . . . . . . . . . . . . .
3.4.1 Dokumentverwaltung . . . . .
.
.
.
.
.
.
.
.
.
.
15
15
15
16
17
17
18
18
18
19
19
.
.
.
.
.
.
25
25
25
26
29
30
31
.
.
.
.
.
.
.
.
.
.
33
33
33
33
34
34
35
35
35
36
36
4 Sichtdefinitionen für NoSQL-Systeme
4.1 Anforderungen an Sichtdefinitionen
4.1.1 Auswahl von Attributen . .
4.1.2 Generierung von Attributen
4.1.3 Selektion von Dokumenten
4.2 Entwicklung einer Sichtdefinition .
4.2.1 Updates auf Sichten . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5 Konzeption einer Sichtenverwaltung in MongoDB
5.1 Sichtenverwaltung . . . . . . . . . . . . . . . .
5.2 Sicht-Anfrage . . . . . . . . . . . . . . . . . . .
5.3 Materialisierte Sichten . . . . . . . . . . . . . .
5.4 Virtuelle Sichten . . . . . . . . . . . . . . . . .
5.5 Architektur . . . . . . . . . . . . . . . . . . . .
5.6 Architektur A: Client-Middleware-Server . . . .
5.6.1 Aufgaben des Clients . . . . . . . . . . .
5.6.2 Aufgaben der Middleware . . . . . . . .
5.7 Architektur B: Client-Server . . . . . . . . . . .
5.8 Vergleich der Architekturen . . . . . . . . . . .
4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Inhaltsverzeichnis
6 Implementierung eines Prototypen für die Sichtenverwaltung
6.1 Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1.1 Projekt aufsetzen . . . . . . . . . . . . . . . . . . .
6.1.2 Erweiterung durch Prototyping . . . . . . . . . . .
6.2 Middleware . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.1 Proxyserver . . . . . . . . . . . . . . . . . . . . . .
6.2.2 Wire Protocol . . . . . . . . . . . . . . . . . . . . .
6.2.3 Interceptor . . . . . . . . . . . . . . . . . . . . . .
6.2.4 Abfangen von Sichten-Operationen . . . . . . . . .
.
.
.
.
.
.
.
.
39
39
39
40
41
41
42
43
44
.
.
.
.
.
.
.
.
49
49
49
49
49
51
51
51
52
8 Zusammenfassung
8.1 Aussichten und Erweiterbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . .
53
53
A MongoDB Wire Protocol
55
B Installation und Nutzung der Middleware
59
Literaturverzeichnis
62
Abbildungsverzeichnis
63
Tabellenverzeichnis
65
Glossar
67
7 Evaluierung
7.1 Bewertung der Sichtdefinitionen . . . . . . . .
7.2 Performance-Analyse . . . . . . . . . . . . . .
7.2.1 Datenbankoperationen . . . . . . . . .
7.2.2 Ansätze zur Performanceverbesserung
7.3 Unit- und Integrationstests . . . . . . . . . .
7.3.1 Unit-Tests . . . . . . . . . . . . . . . .
7.3.2 Integrationstests . . . . . . . . . . . .
7.4 Homogenisierung von Dokumenten . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
in MongoDB
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
Abkürzungsverzeichnis
API
Application Programming Interface
JSON
JavaScript Object Notation
BSON Binary JSON
CQL
Cassandra Query Language
HQL
Hypertable Query Language
SQL
Structured Query Language
SOA
Serviceorientierte Architektur
7
1. Einleitung
In der Regel können NoSQL-Datenbanken als schemalos angesehen werden. Dies bedeutet,
dass die gespeicherten Dokumente beliebig verschachtelte Strukturen aufweisen können und
keinem übergeordneten Schema unterliegen. Die Anwendungen hingegen, welche NoSQLDatenbanken als Speicher verwenden, sind meist auf solch eine Struktur angewiesen, um
den einzelnen Attributen Bedeutungen zuweisen zu können. Ein Problem stellt nun also die
Strukturüberbrückung zwischen einzelnen Datensätzen der gleichen Art dar. Weiterhin ist es
denkbar, dass mehrere Anwendungen die gleichen Basisdaten verwenden sollen, aber jeweils
in einer anderen Repräsentation.
Schaut man in den Bereich der SQL-Datenbanken, so findet man dort zumindest für das
Problem der verschiedenen Repräsentationen bereits eine Lösung, nämlich Sichten (Views).
Das andere Problem der Strukturunterschiede hingegen existiert dort nicht, da alle Datensätze
einem Tabellenschema unterliegen.
Im Rahmen dieser Diplomarbeit wird untersucht, inwiefern das Konzept der SQL-Sichten
auch auf NoSQL-Datenbanken übertragbar ist und ob es auch oder insbesondere für die
Überbrückung der Strukturunterschiede geeignet ist.
Hierzu gilt es zunächst, den Aufbau einer möglichen Sichtdefinition für NoSQL-Systeme zu
beschreiben. Es muss dabei geklärt werden, welche Informationen diese Definition enthalten
soll und in welchem Format sie erstellt und abgespeichert wird.
In einem weiteren Schritt soll untersucht werden, wie die Datensätze gemäß einer dazugehörigen Sichtdefinition transformiert werden können. Anschließend soll die Verwaltung von
Sichtdefinitionen sowie die Ausführung der Transformationen inform eines Prototypen für die
NoSQL-Datenbank MongoDB entwickelt werden.
Der weitere Aufbau dieser Arbeit ist wie folgt unterteilt:
• Kapitel 2: Begleitendes Beispiel
In diesem Kapitel wird ein Beispielszenario beschrieben, welches im Rahmen dieser
Arbeit zur Veranschaulichung von Konzepten dient.
• Kapitel 3: State of the Art
Das dritte Kapitel bietet eine Übersicht über bekannte Konzepte aus dem Datenbankbereich, die als Ausgangspunkt für diese Arbeit dienen.
• Kapitel 4: Sichtdefinitionen für NoSQL-Systeme
Im vierten Kapitel werden Anforderungen für den Aufbau einer Sichtdefinition für
NoSQL-Datenbanken beschrieben. Darauf basierend wird solch eine Sichtbeschreibung
entwickelt.
• Kapitel 5: Konzeption einer Sichtenverwaltung in MongoDB
Das fünfte Kapitel beschreibt die Konzeption einer Sichtenverwaltung für die Doku-
9
1. Einleitung
mentdatenbank MongoDB. Hierzu werden Architekturen vorgestellt, die die Verwaltung
von Sichtdefinitionen und Anfragen auf Sichten ermöglichen sollen.
• Kapitel 6: Implementierung eines Prototypen für die Sichtenverwaltung in
MongoDB
Im sechsten Kapitel werden die Arbeitsschritte beschrieben, mit denen die Implementierung des Prototypen erfolgt. Hierzu werden die ausgearbeiteten Konzepte aus dem
fünften Kapitel angewandt.
• Kapitel 7: Evaluierung
Im siebten Kapitel werden die Konzepte und die prototypische Implementierung bewertet. Es wird diskutiert, inwiefern die Konzepte dieser Arbeit die Probleme der Schemafreiheit von NoSQL-Systemen lösen können.
• Kapitel 8: Zusammenfassung
Das letzte Kapitel fasst die Abläufe und Ergebnisse dieser Arbeit zusammen und gibt
einen Ausblick.
10
2. Begleitendes Beispiel
Zur Veranschaulichung der Konzepte in dieser Arbeit wird ein begleitendes Beispielszenario
entwickelt. Dieses beinhaltet typische Dokumente und Anwendungsfälle für Sichten, wie sie
in heutigen Webanwendungen vorkommen. Die hier vorgestellten Dokumente dienen später
ebenfalls zum Testen der prototypischen Implementierung.
Aktuelle Webanwendungen sind oft modular aufgebaut. Teilweise wird diese Modularisierung auf Software-Ebene erreicht, manchmal aber auch durch dien Aufbau einer Serviceorientierten Architektur (SOA) [16]. Bei letzterem besteht diese - nach außen als Ganzes wirkende
- Anwendung aus mehreren kleineren Service-Anwendungen, die auf bestimmte Aufgabenbereiche spezialisiert sind und über interne Schnittstellen miteinander kommunizieren. Über
die einzelnen Softwaremodule bzw. Services hinweg sollen aber oft die gleichen Basisdaten
verwendet werden.
2.1. Beispielanwendung
In diesem Beispiel gehen wir von einer größeren Webarchitektur aus, die aus drei kleineren
Services (Anwendungen) besteht: einem Blog, einem Onlineshop und einer API zum Datenaustausch mit externen Anwendungen. Die Schichten dieser Architektur und der Datenfluss
zwischen den einzelnen Komponenten ist in Abbildung 2.1 dargestellt.
Als Benutzer werden hier alle Kunden, Mitarbeiter und auch externe Anwendungen bezeichnet, die Daten über eine der bereitgestellten Anwendungen beziehen. Die Anwendungen
bereiten die Rohdaten über die jeweilige Geschäftslogik auf und übernehmen Aufgaben wie
Authentifizierung und Autorisierung. Dadurch wird der Zugriff auf die Bestandsdaten eingeschränkt.
Das Datenbanksystem wird hier durch zwei Schichten dargestellt. Die unterste wird durch
eine NoSQL-Datenbank repräsentiert und stellt nur die Funktionalität zur persistenten Speicherung und Anfrage von Dokumenten bereit. Anwendungen können entweder direkt auf die
heterogenen Daten zugreifen oder über Sichten, welche somit den Zugriff auf homogenisierte
Daten erlauben.
Im Blogmodul dieser Beispielarchitektur veröffentlicht der Seitenbetreiber informative Einträge, die von registrierten Benutzern der Seite kommentiert werden können. Für die Anmeldung im Blogsystem werden Benutzername und Passwort benötigt.
Für den Betrieb des Shopsystems werden unter anderem die, vom Benutzer hinterlegten,
Kreditkartendaten benötigt. Außerdem müssen Produktinformationen verwaltet werden.
Eine API soll Entwicklern externer Anwendungen die Möglichkeit geben, Produktinformationen automatisiert abzufragen. Der Zugriff soll jedoch auf freigeschaltete Produkte beschränkt sein.
11
2. Begleitendes Beispiel
Abbildung 2.1.: Architektur und Datenfluss einer Webanwendung
2.2. Schema-Evolution
Oft verändern sich die Anforderungen an Software im Laufe der Zeit. Durch die Einführung
neuer oder die Erweiterung bestehender Funktionalitäten kann es erforderlich sein, die Schemata der gespeicherten Daten ebenfalls anzupassen. Typischerweise werden einzelne Datensätze in NoSQL-Datenbanken dabei separat versioniert. Die Einführung eines ”version”Attributs zu jedem Dokument ist eine bewährte Methode, um das jeweils verwendete Schema
zu identifizieren [18]. Es beinhaltet die jeweilige Versionsnummer des Schemas, in dem das
Dokument vorliegt. Wird das Dokument einer Schemaänderung unterzogen, so muss die Versionsnummer jeweils inkrementiert werden.
2.3. Verwendete Datenstrukturen
Nachfolgend werden die benötigten Datenstrukturen in Form von JSON-Objekten beschrieben. Es werden Attribute aufgeführt, die für die Veranschaulichung der Beispiele relevant
sind.
Benutzer (Version 1). In Listing 2.1 ist ein Dokument dargestellt, das einen Benutzer
repräsentiert. Es liegt in der Schemaversion 1 vor und beinhaltet die notwendigen Daten für
die Blog- und Shopanwendungen.
12
2.3. Verwendete Datenstrukturen
Listing 2.1: Struktur eines Benutzer-Dokuments, Schemaversion 1
1
{
" name " : " Peter Petersen " ,
" passwort " : " s3cr3t " ,
" kreditkarten " : [
{ " typ " : " visa " , " kartennummer " : "123456789012" }
],
" version " : 1
2
3
4
5
6
7
8
}
Benutzer (Version 2). Im Zuge umfangreicher Softwareänderungen haben sich die Entwickler darauf verständigt, zukünftig nur noch englische Attributbezeichnungen zu verwenden.
Die Daten aller neu registrierten Benutzer werden nach dem neuen, englischsprachigen Schema abgespeichert. Listing 2.2 zeigt solch ein Dokument, welches nach der 2. Schemaversion
erstellt wurde.
Listing 2.2: Struktur eines Benutzer-Dokuments, Schemaversion 2
1
{
" name " : " Hans Hansen " ,
" password " : " t0ps3cr3t " ,
" creditcards " : [
{ " type " : " visa " , " cardnumber " : "987654321098" }
],
" version " : 2
2
3
4
5
6
7
8
}
Benutzer (Version 3). Wenn sich ein Benutzer im Blogsystem anmeldet, soll er mit seinem
Vornamen begrüßt werden. Das Aufteilen des Namens anhand des Leerzeichens erwies sich
in Sonderfällen als unzuverlässig. Es wird eine erneute Schemaänderung vorgenommen. Neue
Benutzer müssen bei ihrer Registrierung ihren Vor- und Nachnamen separat angeben. Existierende Benutzer werden bei ihrer nächsten Anmeldung nach ihrem Vor- und Nachnamen
gefragt und die entsprechenden Daten in die neue Schemaversion überführt. Listing 2.3 stellt
die Benutzerdaten dieses neuen Schemas dar.
Listing 2.3: Struktur eines Benutzer-Dokuments, Schemaversion 3
1
{
" firstname " : " Paul " ,
" lastname " : " Paulsen " ,
" password " : " sup3rs3cr3t " ,
" creditcards " : [
{ " type " : " visa " , " cardnumber " : "314159265359" }
],
" version " : 3
2
3
4
5
6
7
8
9
}
13
2. Begleitendes Beispiel
Produktbeschreibung. Produkte in Shopsystemen werden zumeist durch eine Vielzahl an
Attributen beschrieben. In diesem Beispiel besteht diese Produktbeschreibung lediglich aus
einem Namen, einer Artikelnummer und einem Attribut ”isPublic”. Letzteres wird verwendet, um einzelne Produkte, die noch nicht im Shop angeboten werden sollen, zu markieren. Die API-Komponente für externe Anwendungen soll nur den Zugriff auf freigeschaltete
Artikel ermöglichen. In Listing 2.4 ist die Struktur solch einer Produktbeschreibung dargestellt. In dieser Beispielanwendung gibt es keine weiteren Schemaversionen für Produkte.
Das ”version”-Attribut wird dennoch mit aufgeführt, da eine Versionierung in einer ”echten”
Anwendung vorstellbar ist.
Listing 2.4: Struktur eines Produkt-Dokuments
1
{
" name " : " Rote Hose " ,
" articlenumber " : "23987" ,
" isPublic " : true ,
" version " : 1
2
3
4
5
6
}
14
3. State of the Art
3.1. Sichten in SQL
Im Bereich der SQL-Datenbanken sind Konzepte der Sichtenverwaltung bereits seit langem
bekannt. So sieht der SQL-Standard bereits seit den frühen Versionen Befehle zur Verwaltung
von Sichten vor, wie z.B. CREATE VIEW für das Erstellen einer Sicht.
Zur Veranschaulichung wird in Listing 3.1 eine Sicht namens ”app users v2” erstellt. In
dieser wird aus den Attributen ”firstname” und ”lastname”, welche sich im Basis-Schema
”app users” befinden, per Zeichenketten-Verknüpfung (Concat) das Attribut ”name” erzeugt.
Listing 3.1: Sichtdefinition in SQL
1
2
3
CREATE VIEW app_users_v2 AS
SELECT id , CONCAT ( firstname , " " , lastname ) AS name , password
FROM app_users
Eine Anwendung, welche die Attribute ”id”, ”name” und ”password” erwartet, kann auf die
Sicht ”app users v2” zugreifen, während andere Anwendungen die Basistabelle ”app users”
verwenden können. Dort sind die Attribute ”firstname” und ”lastname” weiterhin separat
enthalten. SQL-Anfragen auf Sichten sind syntaktisch equivalent zu Anfragen auf Tabellen.
3.1.1. Aufgaben von Sichten in SQL
Sichtdefinitionen werden mit Hilfe von SELECT-Anfragen beschrieben. Somit können diese
auch die selben Anfragekomponenten beinhalten, welche nachfolgend beschrieben werden.
Projektion. Die Projektion wird verwendet, um Attribute zu filtern oder neue Attribute
durch arithmetische, Logik- oder Zeichenketten-Operationen zu erzeugen. In SQL wird die
Projektion durch Auflistung der jeweiligen Attribute hinter dem Schlüsselwort SELECT beschrieben.
Selektion. Datensätze, also Zeilen, können über die Selektion gefiltert werden. Hierzu stehen
diverse Vergleichsoperatoren zur Verfügung. Trifft ein Vergleich zu, so resultiert dies in dem
booleschen Wert ”wahr” bzw. ”true”; ansonsten ”falsch” bzw. ”false”. Mehrere solcher Vergleiche können durch logische Operatoren wie AND, OR oder NOT zu komplexeren Ausdrücken
verkettet werden. Wird solch ein Ausdruck auf einen Datensatz angewandt und ergibt einen
wahren Rückgabewert, wird dieser Datensatz in die Ergebnismenge aufgenommen. Die Selektion wird in SQL über die WHERE-Klausel beschrieben.
15
3. State of the Art
Verbund. Die Datensätze mehrerer, möglicherweise normalisierter, Tabellen können mit
Hilfe von Verbundoperationen verknüpft werden. Somit stehen in der Ergebnismenge alle
Attribute und die dazugehörigen Werte der einzelnen Tabellen zur Verfügung. Wie genau die
Verknüpfung der Tabellen durchgeführt wird, hängt von der Art der jeweiligen Verknüpfung
(LEFT JOIN, RIGHT JOIN, INNER JOIN, OUTER JOIN) ab.
Gruppierung und Aggregation. Eine weitere Komponente, die in einem SQL-Query angewandt werden kann, stellt die Aggregation dar. Hierzu lassen sich Aggregatfunktionen
wie Summenbildung (SUM()), Durchschnittsbildung (AVG()) oder die Anzahlbestimmung
(COUNT()) auf die komplette Ergebnismenge oder vorher gebildete Gruppen von Datensätzen
anwenden. Gruppen können mit Hilfe der GROUP BY-Klausel gebildet werden.
3.1.2. Arten von Sichten
Bezüglich des Zeitpunkts ihrer Ausführung werden Sichten in virtuelle und materialisierte
Sichten unterteilt. Ihre unterschiedliche Funktionsweisen werden nachfolgend beschrieben.
Virtuelle Sichten
Virtuelle Sichten werden zum Zeitpunkt der Anfrage aufgelöst. Hierzu wird eine sogenannte
Sichtexpansion auf die Anfrage des Clients durchgeführt. Das Ziel dieser Sichtexpansion ist
es, die einzelnen Anfragekomponenten wie Projektion und Selektion aus der Sichtdefinition
ebenfalls auf die Client-Anfrage anzuwenden. [17]
Die Sichtexpansion kann entweder im Parsebaum, also auf Syntax-Ebene, vorgenommen
werden oder aber im Anfragebaum, welcher die Anfrageoperatoren beinhaltet. Außerdem
muss sie rekursiv angewandt werden, um Szenarien abzudecken, in denen sich Sichten auf
andere Sichten beziehen.
Das Beispiel in Listing 3.2 zeigt eine Anfrage auf die vorher definierte Sicht ”app users v2”
aus Listing 3.1. Die Anfrage enthält eine Projektion auf die Attribute ”id”, ”name” und
”password”, sowie eine Selektion auf das ”status”-Attribut. Nach der Anwendung der Sichtexpansion auf Syntax-Ebene enthält die Anfrage zwei Projektionen, nämlich die der Sicht
und die der Anfrage selbst.
Listing 3.2: Ergebnis einer Sichtexpansion in SQL
1
2
3
4
-- Anfrage auf Sicht " app_users_v2 "
SELECT id , name , password
FROM app_users_v2
WHERE status = " active "
5
6
7
8
9
10
11
12
-- Anfrage nach Anwendung der Sichtexpansion
SELECT id , name , password
FROM (
SELECT id , CONCAT ( firstname , " " , lastname ) AS name , password
FROM app_users
)
WHERE status = " active "
16
3.2. NoSQL-Datenbanken
Materialisierte Sichten
Materialisierte Sichten zeichen sich dadurch aus, dass sie - im Gegensatz zu virtuellen Sichten
- tatsächlich zusätzlichen Speicher für die Datensätze belegen. Sie können als die abgespeicherte Ergebnismenge einer ausgeführten Anfrage zu einem bestimmten Zeitpunkt angesehen
werden. Anfragen auf materialisierte Sichten sind in der Regel schneller als die auf virtuelle
Sichten, da Schritte wie die Sichtexpansion hier entfallen und die Ergebnismenge bereits zu
einem vorherigen Zeitpunkt berechnet wurde.
Um Datensätze einer materialisierten Sicht mit denen der Basistabellen synchron zu halten,
muss die Ergebnismenge stets aktualisiert werden. Hierfür gibt es verschiedene Vorgehensweisen. Eine Neuberechnung der Ergebnismenge kann in definierten Zeitintervallen vorgenommen
werden. Dies hat den Vorteil, dass die Berechnungen asynchron zur Sichtanfrage geschehen,
wodurch die Anfrage selbst schnell ausgeführt werden kann. Allerdings bedeutet dies ebenfalls, dass Datensätze, die nach der letzten Neuberechnung verändert wurden, nicht synchron
sind mit der abgespeicherten Ergebnismenge.
Ein anderer Ansatz hierfür ist die Neuberechnung der Ergebnismenge nach jeder SchreibOperation auf die Basistabellen. Der Vorteil liegt hierbei in der Aktualität der abgespeicherten
Daten der materialisierten Sicht. Dies bringt aber auch den Nachteil mit sich, dass die UpdateOperation selbst mehr Zeit in Anspruch nehmen.
3.2. NoSQL-Datenbanken
Es gibt verschiedene Arten von NoSQL-Datenbanken, die sich unter anderem im Datenmodell, der Anfrage oder ihrer Architektur unterscheiden [13]. Im Rahmen dieser Arbeit richtet
sich der Fokus auf die Dokument- und spaltenorientierten Datenbanken. Diese beiden Datenbanktypen können als schemafrei oder schemaflexibel angesehen werden.
3.2.1. Dokumentdatenbanken
In Dokumentdatenbanken wie MongoDB [9] oder CouchDB [1] werden Daten als SchlüsselWert-Paare gespeichert. Bei dem Schlüssel handelt es sich um eine Zeichenkette, anhand
derer ein Dokument eindeutig identifiziert werden kann. Der Wert stellt hierbei das Dokument dar. Diese Dokumente besitzen eine hierarchische Struktur, wobei jedes Attribut als
Wert entweder eine Liste (Array), ein Objekt (Teildokument) oder einen skalareren Wert
(Zeichenketten, Zahlen, boolesche Werte, Null, etc.) beinhalten kann. Meist wird JSON [20]
als Austauschformat für die Dokumente verwendet.
Die Anfragesprachen unterscheiden sich bei den verschiedenen Datenbanken. So erfordert
bspw. CouchDB das Anlegen von Sichten in Form von Map-Reduce-Funktionen, welche in
JavaScript geschrieben sind. MongoDB hingegen stellt verschiedene APIs für die Abfrage
von (Teil-)Dokumenten bereit. Diese werden im Abschnitt 3.4.1 näher erläutert. Unabhängig
von der Anfragesprache unterstützen Dokumentdatenbanken oft mehrere Indizes, welche die
Selektion durch Attributvergleiche beschleunigen können.
Dokumente werden in Sammlungen (Collections) organisiert, welche mit Tabellen aus SQLDatenbanken vergleichbar sind. Man nutzt diese um gleichartige Dokumente in je einer Collection zusammen zu verwalten. Dennoch unterliegen diese Dokumente keinem übergeordneten
17
3. State of the Art
Schema. Jedes Dokument kann somit eine beliebige Hierarchie aufweisen. Dies bezeichnet
man als schemafrei.
3.2.2. Spaltenorientierte Datenbanken
Spaltenorientierte Datenbanken, auch ”Extensible Record Stores” oder ”Wide Column Stores” genannt, ähneln bzgl. des Datenmodells zunächst den relationalen Datenbanken. Die
Datensätze innerhalb einer Tabelle unterliegen einem Schema. Darüber hinaus können jedoch
auch weitere Spalten pro Datensatz hinzugefügt werden. Dies ist beispielsweise bei HBase [2]
oder BigTable [4] der Fall.
Einen etwas anderen Ansatz hingegen verfolgt CassandraDB [19]. Hier muss zunächst ein
Schema definiert werden und sämtliche Datensätze beinhalten nur eine Untermenge von den
- im Schema festgelegten - Attributen. Das Hinzufügen von Attributen, die nicht Teil des
Schemas sind, ist nicht möglich. Attribute, die in einem Datensatz nicht benötigt werden,
können jedoch leer gelassen werden.
Diese Ansätze der spaltenorientierten Datenbanken zeigen, dass NoSQL-Datenbanken nicht
zwangsläufig schemafrei sind, aber zumindest schemaflexibel.
3.3. NoSQL-Anfragesprachen
Es gibt eine Vielzahl von Anfragemodellen und -sprachen für NoSQL-Systeme. Datenbanken wie CassandraDB [19] oder Hypertable [7] bringen jeweils eine eigene Sprache mit. Mit
der Cassandra Query Language (CQL) und Hypertable Query Language (HQL) wurden zwei
Anfrage- und Updatesprachen entwickelt, die zunächst viel Ähnlichkeit mit der standardisierten Structured Query Language (SQL) aufweisen, sich aber im Detail durch systemspezifische
Einschränkungen oder Erweiterungen unterscheiden.
3.3.1. Map-Reduce
Weit verbreitet ist das Anfrage-Modell ”Map-Reduce”. Das Konzept wurde von Google, Inc.
entwickelt und beschreibt ein Modell, mit dem große Datenmengen parallel in Computerclustern verarbeitet werden können [5].
Die Verarbeitung erfolgt dabei in zwei wesentlichen Schritten, dem Mapping und dem
Reducing.
Schritt 1: Mapping. Beim Mapping werden Datensätze auf Schlüssel-Wert-Paare abgebildet. Diese Abbildung muss nicht zwangsläufig eine 1:1-Abbildung sein. Ein Datensatz kann
beispielsweise auch in mehreren solcher Tupel resultieren. In vielen Datenbanksystemen wird
dieses Mapping durch die Angabe einer JavaScript-Funktion beschrieben, welche auf den
jeweiligen Datenbankservern für jeden Datensatz ausgeführt wird. In diesem JavaScriptKontext steht die entsprechende emit()-Funktion zur Verfügung, mit der solch ein SchlüsselWert-Paar erzeugt werden kann.
Schritt 2: Reducing. Das sogenannte Reducing stellt den zweiten wichtigen Schritt dar. Die
reduce-Funktion wird für jeden Schlüssel, der beim Mapping-Schritt erzeugt wurde, mit allen
18
3.4. MongoDB
dazugehörigen Werten ausgeführt. Dieser Schritt wird ebenfalls oft durch eine JavaScriptFunktion dargestellt. Diese nimmt als Argumente den jeweiligen Schlüssel und eine Liste
(Array) von dazugehörigen Werten entgegen. Der Rückgabewert dieser Funktion stellt dann
einen Datensatz des Endergebnisses dar.
Höhere Programmiersprachen. Ein Vorteil dieses Ansatzes ist, dass innerhalb der mapbzw. reduce-Funktionen ein Großteil des Umfangs der verwendeten Programmiersprache zur
Verfügung steht. Somit sind auch komplexere Berechnungen umsetzbar, die mit Hilfe der
anderen Anfrage-APIs nur aufwendig oder gar unmöglich zu formulieren sind. Dazu zählt
unter anderem der Zugang zu mathematischen Funktionen und Konstanten, sowie Arrayund Zeichenketten-Operationen. Darüber hinaus können meist auch Kontrollstrukturen wie
Schleifen (for, while, do ... while) oder die bedingte Ausführung von Code-Blöcken (if
... else, switch-case) genutzt werden.
3.4. MongoDB
Bei MongoDB [9] handelt es sich um eine schemafreie Dokumentdatenbank. Sie ist auf die
Verwaltung von JSON-basierten Dokumenten ausgelegt und unterstützt Adhoc-Anfragen,
welche im Abschnitt 3.4.1 detaillierter beschrieben sind.
3.4.1. Dokumentverwaltung
Das JSON-Format spielt in MongoDB eine große Rolle. Sowohl die Dokumente selbst als
auch die Anfragen auf die Datenbank erfolgen in Form von JSON-codierten Objekten.
Die Dokumente werden in Sammlungen (Collections) verwaltet, welche vergleichbar sind
mit Tabellen aus dem Bereich der SQL-Datenbanken. Für Anfragen auf die darin gespeicherten Dokumente stehen in MongoDB verschiedene APIs zur Verfügung, welche nachfolgend
vorgestellt werden.
Query-API
Die einfachste API, die MongoDB anbietet, um (Teil-)Dokumente aus einer Collection auszulesen, ist die sogenannte Query-API. In den offiziellen Client-Implementierungen steht diese
API über die find-Methode zur Verfügung. Sie gestattet lediglich die Angabe einer Projektion sowie einer Selektion, welche jeweils über ein JSON-Objekt beschrieben werden können.
Selektion. Für Selektionen stehen u. a. Vergleichsoperatoren wie $eq (Test auf Gleichheit),
$exists (Test auf Existenz eines Attributs) oder $gt (größer als), sowie die Verknüpfung dieser Vergleiche durch $and (Verundung) bzw. $or (Veroderung) zur Verfügung. Diese können
genutzt werden, um Dokumente zu filtern.
Projektion. Die Projektion erfolgt auf Attribute, welche entweder einen skalaren Wert oder
wiederum ein Teildokument beinhalten können. Pfade zu einem Attribut lassen sich durch
Auflistung aller einzelnen Attributsnamen dorthin beschreiben. So führt bspw. der Pfad a.b.c
19
3. State of the Art
in dem Dokument in Listing 3.3 zu dem skalaren Wert ”foo”, während der Pfad a.b das
Teildokument unterhalb des b-Attributes enthält.
Listing 3.3: Projektion in MongoDB
1
2
3
4
5
6
7
8
9
10
// Dokument
{
"a": {
"b": {
" c " : " foo " ,
" d " : " bar "
}
},
" e " : " baz "
}
11
12
13
14
15
16
17
18
19
// Projektion auf " a . b . c " ergibt :
{
"a": {
"b": {
" c " : " foo "
}
}
}
20
21
22
23
24
25
26
27
28
29
// Projektion auf " a . b " ergibt :
{
"a": {
"b": {
" c " : " foo " ,
" d " : " bar "
}
}
}
30
31
32
33
34
// Projektion auf " e " ergibt :
{
" e " : " baz "
}
Weiterhin ist die Projektion lediglich auf die Anwesenheit oder Abwesenheit von Attributen beschränkt. Umfangreichere Operationen wie die Umbenennung von Attributen oder
Anwendung von Aggregatsfunktionen sind hier nicht möglich.
Das Beispiel 3.4 zeigt eine Anfrage mit Hilfe der Query-API auf die Collection ”app users”.
In dieser Anfrage werden Dokumente selektiert, deren Attribut ”firstname” dem Wert ”Max”
oder deren Attribut ”status” dem Wert ”active” entspricht.
20
3.4. MongoDB
In der Projektionsbeschreibung werden alle Attribute mit dem Wert 1 markiert, welche
im Ergebnis zurückgegeben werden sollen. Attribute, die den Wert 0 zugewiesen bekommen,
erscheinen nicht im Ergebnis. Eine Ausnahme stellt die generierte Objekt-ID unter dem Attribut id dar, welche automatisch im Ergebnis erscheint, wenn sie nicht explizit als abwesend
markiert wird.
Listing 3.4: find-Anfrage in MongoDB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
db . collection ( " app_users " ) . find (
// Selektion
{
" $or " : [
{
" firstname " : " Max "
},
{
" status " : " active "
}
]
},
// Projektion
{
" firstname " : 1 ,
" lastname " : 1 ,
" password " : 1 ,
" _id " : 0
}
);
Aggregation-Pipeline
Seit der Version 2.2 ist in MongoDB die sogenannte ”Aggregation-Pipeline” [10] verfügbar.
Eine Anfrage über diese API gestattet die Angabe einer oder mehrerer Pipelines, welche
nacheinander ausgeführt werden. Innerhalb dieser Pipelines stehen diverse Anfrageoperatoren
zur Verfügung, die nachfolgend beschrieben werden.
Projektion. Über die Angabe des Schlüssels $project kann die Projektion beschrieben werden. Über die Angabe bzgl. der An- bzw. Abwesenheit einzelner Attribute hinaus, ist die Umbenennung von vorhandenen Attributen und die Erzeugung neuer Attribute möglich. Hierzu
stehen u. a. arithmetische Operationen und Zeichenketten-Funktionen wie $concat (Verkettung von Zeichenketten) zur Verfügung.
Selektion. Die Beschreibung einer Selektion kann über den Schlüssel $match zur Pipeline
hinzugefügt werden. Diese entspricht dem Aufbau der Selektion der Query-API aus Abschnitt 3.4.1.
Gruppierung und Aggregate. Analog zur GROUP BY-Klausel aus SQL sind auch in MongoDB Gruppierungen von Dokumenten über die Angabe des Schlüssels $group möglich. Diese
21
3. State of the Art
Gruppen können anschließend für die Bildung von Aggregaten verwendet werden, wie z.B.
die Summe ($sum) oder der Durchschnitt ($avg).
Weitere Operatoren. Weiterhin können Dokumente anhand eines oder mehrerer Attribute
sortiert werden ($sort). Die Angabe des Schlüssels $out und dem Namen einer Collection
als Wert sorgt dafür, dass das Ergebnis der aggregate-Anfrage nicht zurückgegeben wird,
sondern stattdessen in der angegebenen Collection abgespeichert wird.
Im Gegensatz zu SQL-Datenbanken stehen in MongoDB keine Verbund-Operationen (Joins)
zur Verfügung. Eine Verknüpfung von Dokumenten aus mehreren Collections muss durch die
Anwendung selbst erfolgen. Als Alternative gibt es Objektreferenzen. Ein Attribut kann somit eine Referenz auf ein anderes Objekt (Dokument) aus der selben oder einer anderen
Collection enthalten. Da es sich jedoch nur um eine Referenz handelt, muss der Client das
referenzierte Dokument in einer separaten Anfrage holen.
Listing 3.5 zeigt, wie eine Anfrage mit Hilfe der Aggregation-Pipeline erfolgt. Die Selektion
entspricht der aus dem Beispiel der Query-API (3.4). In der Projektion wird hier definiert,
dass das Attribut ”name” aus den Werten der Attribute ”firstname” und ”lastname”, getrennt
durch ein Leerzeichen, zusammengesetzt wird. Das Feld ”status” wird übernommen und die
automatisch generierte Dokument-ID wird als abwesend markiert.
Listing 3.5: aggregate-Anfrage in MongoDB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
db . collection ( " app_users " ) . aggregate ([
{
// Projektion
" $project " : {
" name " : {
" $concat " : [ " $firstname " , " " , " $lastname " ]
},
" password " : 1
, " _id " : 0
}
},
{
// Selektion
" $match " : {
" $or " : [
{
" firstname " : " Max "
},
{
" status " : " active "
}
]
}
}
]) ;
22
3.4. MongoDB
Map-Reduce
MongoDB stellt ebenfalls eine Map-Reduce-API bereit. Das Map-Reduce-Modell wurde in
Abschnitt 3.3.1 bereits vorgestellt. Die map- und reduce-Funktionen werden in MongoDB
jeweils in Form einer JavaScript-Funktion definiert.
Weitere Anfrageoperatoren. Neben der Selektion, die sich durch die JavaScript-Funktionen
formulieren lässt, können Dokumente beim Verwenden der Map-Reduce-API bereits im Vorfeld durch Angabe eines zusätzlichen Query-Ausdrucks gefiltert. Dies erlaubt eine effizientere
Selektion durch die Nutzung von Indizes auf entsprechenden Attributen. Nach der Ausführung
der Map-Reduce-Schritte kann das Ergebnis auch sortiert oder in der Anzahl limitiert werden.
23
4. Sichtdefinitionen für NoSQL-Systeme
Wie in den Abschnitten 3.2 und 3.3 erwähnt, gibt es für die unterschiedlichen NoSQLSysteme auch verschiedene Anfrage- und Updatesprachen. In diesem Kapitel soll der Aufbau
einer Sichtdefinition erarbeitet werden.
4.1. Anforderungen an Sichtdefinitionen
In Datenbanksystemen, die bereits eine Sichtenverwaltung besitzen, werden Sichten in der
Regel in Form einer Anfrage beschrieben. In SQL-Datenbanken handelt es sich dabei um
SELECT-Anfragen. Sichten in CouchDB werden durch Map-Reduce-Funktionen abgebildet [8].
Dieser Ansatz soll in diesem Kapitel ebenfalls verfolgt werden. Dies bedeutet jedoch auch,
dass die Menge der möglichen Sichtoperationen vom Beschreibungsumfang der jeweiligen Anfragesprachen beschränkt wird. Eine Sicht kann somit nur die Transformationen vornehmen,
die sich durch eine Anfrage formulieren lassen.
Um eine systemunabhängige Sichtdefinition zu beschreiben, ist es notwendig, einige Anforderungen an die NoSQL-Datenbanken zu stellen. Nachfolgend werden die angestrebten
Transformationsschritte anhand von Beispielen beschrieben und die dazu notwendigen Voraussetzungen erläutert.
4.1.1. Auswahl von Attributen
Zu den Aufgaben einer Projektion gehört die Filterung von Attributen. Es soll möglich sein,
einzelne Attribute aufzuzählen, die in den Ergebnisdokumenten (bzw. -datensätzen) vorhanden sind.
Zum Vergleich: In SQL-verwandten Sprachen wie CQL oder HQL erfolgt die Projektion in
der Regel über die SELECT-Klausel, wie in Listing 4.1 dargestellt.
Listing 4.1: Einfache Projektion von Attributen in SQL-verwandten Sprachen
1
SELECT name , password
Die meisten Dokumentdatenbanken unterstützen eine solche Projektion, z.B. über entsprechende Map-Reduce-Funktionen oder die zusätzliche Angabe einer Projektion wie bei
MongoDB (siehe Abschnitt 3.4.1).
Beispiel: Filtern von Attributen. Die Webanwendung aus dem Beispielszenario (Kapitel 2)
verwaltet Kreditkarteninformationen in den Benutzer-Objekten. Aus Sicherheitsgründen soll
das Blogsystem nur Zugriff auf die Daten haben, die für die Anmeldung notwendig sind. Eine
Projektionssicht soll dafür sorgen, dass nur die Attribute ”name” und ”password” in den
Anfrageergebnissen enthalten sind. Listing 4.2 zeigt das erwartete Ergebnis.
25
4. Sichtdefinitionen für NoSQL-Systeme
Listing 4.2: Auswahl von Attributen
1
{
" name " : " Peter Petersen " ,
" password " : " s3cr3t "
2
3
4
}
4.1.2. Generierung von Attributen
Eine weitere Aufgabe der Projektion ist es, neue Attribute zu generieren. Nachfolgend werden
drei Anwendungsbeispiele beschrieben, in denen es nützlich sein kann, Attribute zu erzeugen.
Attribut mit konstantem Wert
Ein Attribut kann einen konstanten Wert (Zahl, Boolean oder Zeichenkette) zugewiesen bekommen. Wenn das Attribut bereits vorher existiert, wird dessen Wert in diesem Schritt
überschrieben. Existiert das Attribut noch nicht, so wird es erzeugt. Dies ist nützlich, wenn
die Anwendung das Vorhandensein eines Attributs erfordert, welches im Ausgangsschema
nicht vorhanden ist.
Eine vergleichbare SQL-Anfrage könnte so aussehen, wie in Listing 4.3 dargestellt. In dieser
Projektion werden zwei Attribute erzeugt und ihnen eine Zeichenkette und eine Fließkommazahl als Wert zugewiesen.
Listing 4.3: Erzeugung eines Attribut mit konstantem Wert in SQL-verwandten Sprachen
1
SELECT " 1970 -01 -01 " AS birthday , 3.141 AS pi
Beispiel: Attribut mit konstantem Wert. In der Beispielanwendung wird eine externe Bibliothek verwendet. Diese kann oder darf nicht verändert werden. Sie ist für ein Schema konzipiert worden, welche das Geburtsdatum eines Benutzers im Attribut ”birthday” beinhaltet.
Das Geburtsdatum wird sonst an keiner Stelle der Anwendung benötigt und somit auch nicht
gespeichert. Das Attribut für den Geburtstag kann mit einem Dummy-Wert erzeugt werden.
Das zu erwartende Ergebnis ist in Listing 4.4 dargestellt.
Listing 4.4: Auswahl von Attributen
1
{
" name " : " Hans Hansen " ,
" password " : " t0ps3cr3t " ,
" creditcards " : [
{ " type " : " visa " , " cardnumber " : "987654321098" }
],
" version " : 2 ,
" birthday " : "1970 -01 -01"
2
3
4
5
6
7
8
9
}
26
4.1. Anforderungen an Sichtdefinitionen
Attributkopie
Ein Attribut kann mit dem Wert eines existierenden Attributs erzeugt werden. Dabei wird
der Wert des vorhandenen Attributs zum neu erzeugten kopiert.
Beispiel: Umbenennung von Attributen. Die Umbenennung von Attributen stellt eine
wichtige Aufgabe von Sichten da. Erst dadurch ist es möglich, gleiche Basisdaten auf verschiedene Weisen zu repräsentieren. Eine Umbenennung erfolgt durch das Kopieren eines
Attributwerts zu einem neuen Attribut und das Weglassen des originalen Attributnamens
aus der Projektion.
Ein Anwendungsfall hierfür ist die Transformation eines Benutzer-Dokuments der ersten
Schemaversion aus dem Beispielszenario in die zweite. In Listing 4.5 ist zunächst das Ergebnis
der Attributkopie zu sehen. Es werden die deutschen Attributbezeichnungen in die englischen
übersetzt und die jeweiligen Werte kopiert.
Listing 4.5: Erzeugung von Attributen durch Kopie
1
{
" name " : " Peter Petersen " ,
" passwort " : " s3cr3t " ,
" kreditkarten " : [
{ " typ " : " visa " , " kartennummer " : "123456789012" }
],
" version " : 1 ,
" password " : " s3cr3t " ,
" creditcards " : [
{ " type " : " visa " , " cardnumber " : "123456789012" }
]
2
3
4
5
6
7
8
9
10
11
12
}
Wendet man nun noch die bereits vorgestellten Transformationsschritte aus Abschnitt 4.1.1
(Auswahl von Attributen) und 4.1.2 (Attribut mit konstantem Wert) an, so lässt sich ein
Ergebnis, wie in Listing 4.6 dargestellt, konstruieren.
Die Projektion beinhaltet nur noch die Attribute ”name”, ”password”, ”creditcards” und
”version”. Die Attribute ”passwords” und ”creditcards” (zusammen mit den darunterliegenden Objekten) sind durch die Attributkopie entstanden. Die Versionsnummer wurde mit Hilfe
eines konstanten Wertes überschrieben.
Auf diese Weise kann ein Dokument so transformiert werden, dass es der zweiten Schemaversion entspricht.
Listing 4.6: Anwendung von Attributkopie, Projektion und konstantem Attributwert
1
2
3
4
5
6
{
" name " : " Peter Petersen " ,
" password " : " s3cr3t " ,
" creditcards " : [
{ " type " : " visa " , " cardnumber " : "123456789012" }
],
27
4. Sichtdefinitionen für NoSQL-Systeme
" version " : 2
7
8
}
Attribute durch String-Verknüpfung
Eine weitere Art, Attributwerte zu erzeugen, ist die Verkettung von Zeichenketten. Dabei
können die Zeichenketten konstant sein oder aus anderen Attributwerten bestehen. Hierdurch
lassen sich normalisierte Attribute wieder vereinen. Das nachfolgende Beispiel veranschaulicht
diesen Schritt anhand der Beispielanwendung.
Beispiel: Denormalisierung von Attributen Ein Modul der Webanwendung ist bereits veraltet und soll demnächst durch ein neues Modul abgelöst werden. Dieses ältere Modul benötigt
das Benutzer-Schema in der zweiten Version, bei dem der vollständige Benutzername in einem einzigen Attribut steckt. Bis zum Austausch dieses Moduls muss es weiterhin möglich
sein, den Vor- und Nachnamen aus der dritten Schemaversion zu diesem einen Attribut zu
vereinen. Listing 4.7 zeigt das zu erwartende Ergebnis aus dieser Art der Attribut-Erzeugung.
Listing 4.7: Erzeugung eines Attributs durch String-Verknüpfung
1
{
" name " : " Paul Paulsen " ,
" password " : " sup3rs3cr3t " ,
" creditcards " : [
{ " type " : " visa " , " cardnumber " : "314159265359" }
],
" version " : 3
2
3
4
5
6
7
8
}
Bedingte Werterzeugung
Eine Problemstellung, die insbesondere in NoSQL-Systemen über Sichttransformationen gelöst
werden soll, ist die Überbrückung von Strukturunterschieden zwischen mehreren Dokumenten einer Art. Um gleichstrukturierte Dokumente als Anfrage-Ergebnis zu erhalten, müssen
Attributwerte abhängig vom aktuellen Schema erzeugt werden. Die Erfüllung oder Nichterfüllung einer Bedingung soll darüber entscheiden, welcher von zwei möglichen Werten als
Attributwert verwendet wird.
In der Beispielanwendung wurde bereits eine Versionierung über das zusätzliche ”version”Attribut in allen Dokumenten vorgenommen. Anhand dessen Wertes soll es möglich sein,
weitere Attribute zu definieren.
Beispiel: Bedingte Werterzeugung Die Entwickler der vorgestellten Webarchitektur müssen
mehrere einzelne Anwendungen pflegen. Eine Schematransformation innerhalb der Anwendungen ist zwar umsetzbar, bringt jedoch einige Nachteile mit sich. So müssen die Transformationsregeln in jeder Anwendung separat implementiert werden. Mit jeder neu eingeführten
Schemaversion wächst die Komplexität innerhalb der Anwendungen. Es wird gewünscht, dass
28
4.1. Anforderungen an Sichtdefinitionen
diese Schematransformationen außerhalb der Anwendung erfolgen. So kann für jede genutzte
Schemaversion eine eigene Sicht erstellt werden.
Möchte man Dokumente aus der dritten Schemaversion in die zweite transformieren, so
kann dies mit dem Pseudocode aus Listing 4.8 umgesetzt werden. Hierzu wird ein Konstrukt
der Form IF Bedingung THEN Ausdruck1 ELSE Ausdruck2 ENDIF benötigt. Die Bedingung muss, wie die Selektion, durch Wertevergleiche formuliert werden können. Die Ausdrücke
hingegen können konstante oder konkatenierte Werte darstellen oder andere existierende Attribute.
Auch eine Verschachtelung von IF ... ELSE-Blöcken ist denkbar. Hierdurch können auch
Vergleiche mit weiteren Versionen erfolgen. So kann eine Vielzahl an Schemaversionen auf ein
Zielschema transformiert werden.
Listing 4.8: Pseudocode für die Transformation eines Schemas
1
2
3
4
5
6
7
8
9
PROJECT
( IF version = 3 THEN
CONCAT ( firstname , " " , lastname )
ELSE
name
ENDIF ) AS name ,
password ,
creditcards ,
2 AS version
Die Bedingungsprüfung innerhalb der Projektion ist notwendig für diese Art der Transformation. Nur wenige NoSQL-Anfragesprachen bieten die Möglichkeit, diese zu formulieren.
Systeme mit einer Map-Reduce-Schnittstelle unterstützen in der Regel solche Ausdrücke. Die
Aggregation-API von MongoDB sieht ebenfalls solch eine Projektion über eine entsprechende
JSON-Struktur vor.
4.1.3. Selektion von Dokumenten
Es gibt Anwendungsfälle, in denen bereits auf Datenbank-Ebene Datensätze selektiert werden sollen. Über eine Sichttransformation muss es möglich sein, Datensätze und Dokumente
anhand von Attributwerten zu filtern. Es genügt zunächst die Voraussetzung, dass Attributwerte auf Gleichheit geprüft werden können. Mehrere solcher Attributvergleiche sollen auf ein
Dokument angewendet werden können. Nur wenn alle Vergleiche zutreffen, soll der Datensatz
in der Ergebnismenge aufgenommen werden (UND-Verknüpfung).
Beispiel: Selektion von Dokumenten. Die API der Webanwendung soll nur den Zugriff auf
freigeschaltete Produktinformationen gestatten. Damit versehentliche Implementierungsfehler innerhalb der API-Komponente nicht dazu führen, dass noch nicht-öffentliche Produkte
frühzeitig in Erfahrung gebracht werden können, soll eine Selektion bereits vorher stattfinden.
Die API-Komponente bekommt nur die Berechtigung, lesend auf diese Sicht zuzugreifen. Ein
Direktzugriff auf die Basisdaten ist ausgeschlossen.
Die meisten NoSQL-Systeme bieten die Möglichkeit, solch eine Selektion über eine Anfragesprache zu beschreiben.
29
4. Sichtdefinitionen für NoSQL-Systeme
4.2. Entwicklung einer Sichtdefinition
Als Ausgangsbasis für eine Sichtdefinition soll eine Anfragebeschreibung dienen. Diese sollte
möglichst systemunabhängig sein, was sich jedoch als schwierig erweist, da es keine einheitliche API für Anfragen in NoSQL-Datenbanken gibt. Deshalb wird nun versucht, eine
existierende Anfrage-API dahingehend zu erweitern, dass Sichtdefinitionen mit ihr verwaltet
und Sichten angefragt werden können.
Die Query- und Aggregation-API von MongoDB scheinen, hierfür geeignet zu sein.
Wie im Abschnitt 3.4.1 bereits dargelegt wurde, unterstützt die Query-API von MongoDB
keine Möglichkeiten der Umbenennung von Attributen. Diese sind jedoch essenziell für die
Transformation eines Ausgangsschemas in ein Zielschema. Erst die, in Abschnitt 3.4.1 beschriebene, Aggregation-Pipeline stellt diese erweiterte Projektion zur Verfügung und dient
somit als Grundlage für die Sichttransformationen.
Anfragen werden in MongoDB in Form von JSON-Objekten beschrieben. Analog dazu kann
eine Sichtendefinition ebenfalls durch solch ein Anfrageobjekt beschrieben werden.
Listing 4.9 stellt dar, wie eine API zum Anlegen einer Sichtdefinition aussehen und verwendet werden könnte. Es wird der Name der Sicht und die Zielcollection benötigt, sowie die
Anfrage selbst. Außerdem muss ein Flag angegeben werden, welches signalisiert, ob es sich
dabei um eine virtuelle oder eine materialisierte Sicht handelt.
Listing 4.9: API zum Anlegen einer Sichtdefinition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
db . createView (
// Name der Sicht
" app_users_v2 " ,
// Zielcollection
" app_users " ,
// Definition ( Aggregate - Anfrage )
[
{
" $project " : {
" name " : {
" $ifNull " : [
" $name " ,
{ " $concat " : [ " $firstname " , " " , " $lastname " ]}
]
},
" password " : 1 ,
" _id " : 0
}
}
],
// materialisiert
true
);
30
4.2. Entwicklung einer Sichtdefinition
4.2.1. Updates auf Sichten
Update-Operationen auf Sichten werden im Rahmen dieser Arbeit nicht behandelt. Hierfür
wäre es notwendig, 1:1-Abbildungen des Originalschemas zum transformierten Schema zu
schaffen. Diese müssten bei einem Update zurückgeführt werden, was - abhängig von den
Transformationsschritten - nur schwer oder gar nicht umsetzbar ist. Hierzu zählen Attribute,
die durch Aggregationen oder die Verkettung von Zeichenketten entstanden sind.
31
5. Konzeption einer Sichtenverwaltung in
MongoDB
In diesem Kapitel wird nachfolgend das Konzept für die Implementierung des Prototyps
beschrieben. Dabei wird zunächst auf die Verwaltung der Sichtdefinition eingegangen. Anschließend werden verschiedene Architekturen vorgestellt und bewertet.
5.1. Sichtenverwaltung
Das Format für die Sichtendefinition wurde in Abschnitt 4.2 auf JSON-codierte Anfrageobjekte festgelegt. Dies entspricht somit genau dem gleichen Format, in dem auch Dokumente
gespeichert werden. Sichtendokumente können somit in einer extra Collection verwaltet werden.
5.2. Sicht-Anfrage
Die Anfrage auf eine Sicht erfolgt durch das Absetzen einer Query-Operation mit dem Namen
der Sicht und der Zeichenkette ”VIEW:” als Präfix, z.B. ”datenbankname.VIEW:app user v2”.
Bei der Betrachtung der verschiedenen Anfrage-APIs (Abschnitt 3.4.1) fällt auf, dass nicht
alle APIs für die prototypische Implementierung geeignet sind. Es wird mindestens eine API
mit dem Umfang der Aggregation-Pipeline benötigt, um solch eine Sichtenverwaltung umzusetzen. Die Query-API, welche nur Selektion und Projektion unterstützt, stellt somit eine
Untermenge der möglichen Anfragekomponenten der Aggregation-Pipeline dar. Dies bedeutet, dass eine Anfrage, die über die Query-API abegsetzt wird, innerhalb des Prototypen in
eine Anfrage über die Aggregation-Pipeline übersetzt und ausgeführt werden kann.
Die dritte vorgestellte Anfrage-API, Map-Reduce, wird aufgrund ihrer hohen Komplexität
im Rahmen dieser Arbeit nicht weiter betrachtet.
5.3. Materialisierte Sichten
Materialisierte Sichten können durch die Ausführung einer Sicht-Anfrage und anschließendem
Abspeichern des Ergebnisses erstellt werden. Die Aggregation-Pipeline stellt ein optionales
Attribut $out zur Verfügung, worunter sich eine Zielcollection angeben lässt. Dort wird dann
das Ergebnis der Anfrage gespeichert.
Um dieses gespeicherte Ergebnis mit den Basisdaten zu synchronisieren, muss diese Anfrage
wiederholend ausgeführt werden. Dies kann entweder in einem festgelegten Intervall geschehen
oder nach jedem Update auf die Basiscollection. Ersteres kann bspw. durch ein dauerhaft
laufendes Programm (Daemon) geschehen. Dieses setzt mit Hilfe eines Intervall-Timers die
selbe Anfrage in vorgegebenen Zeitabständen regelmäßig ab.
33
5. Konzeption einer Sichtenverwaltung in MongoDB
Updates auf die Basisdaten lassen sich hingegen schwieriger erfassen. Hierzu müsste - bei
jeglicher Änderung eines Dokuments - der Client selber ein dauerhaft laufendes Programm
über die erfolgte Schreiboperation informieren. Alternativ kann ein Proxyserver zwischen
Client und MongoDB-Server geschaltet werden, welcher sämtliche Befehle decodiert und im
Falle von Update-Operationen die jeweiligen Sichtanfragen erneut ausführt.
5.4. Virtuelle Sichten
Virtuelle Sichten werden erst bei der eigentlichen Anfrage durch den Client ausgeführt.
Die hierfür erforderliche Sichtexpansion kann im Falle von MongoDB bereits mit Hilfe der
Aggregation-Pipeline umgesetzt werden, da es auch gestattet ist, mehrere Pipelines anzugeben, die hintereinander ausgeführt werden.
Somit lässt sich die Sichtexpansion durch eine Nacheinanderausführung der Sichtdefinition
sowie der eigentlichen Anfrage realisieren. Listing 5.1 zeigt, wie solch eine Anfrage manuell
ausgeführt werden kann. Der Prototyp, welcher in dieser Arbeit entwickelt wird, muss nun
lediglich Anfragen, welche über die Query-API bzw. die Aggregation-Pipeline gestellt werden,
in solch eine erweiterte Anfrage überführen und an den MongoDB-Server senden.
Listing 5.1: Aggregation mit zwei Pipelines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
db . benutzer . aggregate ([
// 1. Pipeline ( Sichtdefinition )
{
" $project " : {
" name " : {
" $ifNull " : [
" $name " ,
{ " $concat " : [ " $firstname " , " " , " $lastname " ]}
]
},
" password " : 1 ,
" _id " : 0
}
},
// 2. Pipeline ( Anfrage )
{
" $project " : {
" nombre " : " $name "
}
}
]) ;
5.5. Architektur
Es werden zwei Architekturen in Betracht gezogen, mit denen sich MongoDB um eine Sichtenverwaltung erweitern lässt. In Abschnitt 5.6 wird eine Client-Middleware-Server-Architektur
vorgestellt, bei der die Sichtenlogik von der Middleware umgesetzt wird. Als Alternative dazu
wird im Abschnitt 5.7 eine Client-Server-Architektur erläutert, bei welcher der Client selbst
34
5.6. Architektur A: Client-Middleware-Server
die Sichtenverwaltung umsetzt. Im Anschluss daran werden beide Architekturen im Abschnitt
5.8 direkt miteinander verglichen und die jeweiligen Vor- sowie Nachteile aufgezeigt.
5.6. Architektur A: Client-Middleware-Server
Für die Client-Middleware-Server-Architekture (Abb. 5.6) muss der MongoDB-Client um die
Methoden zum Erstellen und Löschen von Sichtendefinitionen erweitert werden. Zudem wird
eine Middleware, ein spezieller Proxyserver, benötigt. Diese Middleware wird zwischen den
Client und Datenbankserver geschaltet, wo sie für die Logik rund um die Verwaltung von
Sichtendefinitionen, sowie Anfragen auf Sichten verwantwortlich ist.
Abbildung 5.1.: Architektur A: Sichtenverwaltung durch Middleware
Die Sichtdefinitionen selbst liegen in Form von Dokumenten vor. Sie können über die
nativen Operationen OP INSERT, OP UPDATE und OP DELETE - wie jedes andere Dokument
auch - verwaltet werden. Eine Erweiterung des Protokolls ist somit nicht notwendig.
5.6.1. Aufgaben des Clients
Die API des Clients wird um drei Methoden erweitert: createView(name, query, isMaterialized)
zum Erstellen, updateView(name, query, isMaterialized) zum Updaten einer vorhandenen Sicht und deleteView(name) zum Löschen einer Sichtdefinition. Dies sind lediglich
Wrapper-Methoden für insert(document, options), update(selector, document[, options])
und remove([selector][, options]) des Standard-Clients.
5.6.2. Aufgaben der Middleware
Die Middleware muss in der Lage sein, eingehende Befehle des Clients zu decodieren und je
nach Art des Befehls, wie nachfolgend beschrieben, darauf reagieren:
Handelt es sich um Befehle der Sichtenverwaltung (Einfügen, Verändern, Löschen), so werden diese Operationen zwar ausgeführt, aber zusätzlich auch die dazugehörigen materialisierten Sichten, wie in Abschnitt 5.3 beschrieben, erstellt bzw. gelöscht.
Erfolgt hingegen eine Anfrage, so wird rekursiv eine Sichtexpansion auf selbige ausgeführt.
Im Falle einer materialisierten Sicht bedeutet es, dass lediglich der Name der Zielcollection umgeschrieben wird. Sollte aber eine virtuelle Sicht angefragt worden sein, wird, wie in
Abschnitt 5.4 beschrieben, die Aggregation-Pipeline ausgeführt.
35
5. Konzeption einer Sichtenverwaltung in MongoDB
Sämtliche anderen Operationen werden transparent an den Server weitergeleitet. Die jeweiligen Antworten des Servers werden zurück an den Client gesendet.
5.7. Architektur B: Client-Server
Die Client-Server-Architektur (Abb. 5.7) stellt die übliche MongoDB-Architektur dar. Um
hier eine Sichtenverwaltung vorzunehmen, ohne den Server selbst zu verändern, muss der
Client die komplette Logik ausführen. Auch hier kann, wie schon bei der Client-MiddlewareArchitektur erwähnt, die Verwaltung der Sichtendefinitionen mit Hilfe nativer Operationen
umgesetzt werden. Dazu ist es nicht notwendig, das bestehende Übertragungsprotokoll anzupassen. Die Sichtexpansion findet ebenfalls im Client statt.
Abbildung 5.2.: Architektur B: Sichtenverwaltung im Client
5.8. Vergleich der Architekturen
Stellt man beide Architekturen gegenüber, so ergibt sich daraus folgende Zusammenfassung
(Tabelle 5.1):
Tabelle 5.1.: Vergleich der Architekturen zur Sichtenverwaltung
Eigenschaft
Client-Middleware-Server
Client-Server
ProjektOrganisation
2 Projekte (je eins für Client und
Middleware), die aufeinander abgestimmt und getestet werden müssen
nur 1 Projekt
Wiederverwendbarkeit
Middleware kann ”wiederverwendet” werden; Clients in anderen
Sprachen müssen nur WrapperMethoden implementieren
nicht gegeben; Sichtenlogik muss für
jeden Client neu implementiert werden muss
Middleware kann Autorisierung
übernehmen, somit kann Zugriff
auf einzelne Sichten eingeschränkt
werden
Nicht möglich, da Client allein für
Sichtenverwaltung zuständig
Autorisierung
Sichten
36
für
5.8. Vergleich der Architekturen
Aus Sicht der Entwicklung, Testbarkeit und Projektorganisation liegen die Vorteile bei der
Client-Server-Architektur. So muss z.B. die Projektstruktur nur einmal erstellt werden. Der
Aufwand für die Versionskontrolle fällt geringer aus und es können Integrationtests für die
komplette Komponente entwickelt werden.
Auf der anderen Seite überwiegen die Vorteile der Erweiterbarkeit der Client-MiddlewareServer-Architektur. Weitere Clients lassen sich mit weniger Aufwand entwickeln. Der Client
bleibt ”leichtgewichtig” und vermittelt lediglich Befehle, anstatt selbst komplexere Operationen auszuführen. Die Middleware kann durch zusätzliche Features wie z.B. einer Autorisierung für Verwaltung und Nutzung von Sichten erweitert werden.
Nachfolgend wird deshalb nur noch die Variante mit der Middleware betrachtet.
37
6. Implementierung eines Prototypen für die
Sichtenverwaltung in MongoDB
Quelloffene Client-Implementierungen für MongoDB sind in vielen höheren Programmiersprachen verfügbar. Für einen Proxyserver scheint eine ereignisorientierte Programmiersprache geeignet zu sein. Deshalb wird für die Umsetzung der Middleware Node.js verwendet.
Der Quellcode für den MongoDB Native Driver ist auf GitHub [6] veröffentlicht.
Die Server-Implementierung von MongoDB wird im Laufe dieser Arbeit nicht verändert,
was den Vorteil hat, dass die Serverversion jederzeit ausgetauscht werden kann. Somit ist
die Middleware zu allen MongoDB-Servern ab Version 2.2 kompatibel. Es wird die aktuelle
Serverversion 2.6.1 verwendet.
Die Implementierung für den Client und die Middleware erfolgt separat. Deshalb wird
nachfolgend zunächst die Umsetzung für die Erweiterung des offiziellen Node.js-Clients für
Mongo DB beschrieben. Anschließend werden im zweiten Abschnitt 6.2 die Arbeitsschritte
zur Erstellung der Middleware geschildert.
6.1. Client
Die Entwicklung des Clients erfolgt in mehreren Schritten. Im ersten Schritt (Abschnitt 6.1.1)
wird ein neues Projekt aufgesetzt und benötigte npm-Module werden installiert. Danach
erfolgt die eigentliche Erweiterung des Clients.
6.1.1. Projekt aufsetzen
Um den offiziellen MongoDB-Client für Node.js mittels npm zu installieren, wird eine Paketdefinition (package.json) im Hauptverzeichnis des Projekts angelegt. Dabei handelt es
sich um eine JSON-formatierte Konfiguration für npm-Module, welche unter anderem Informationen wie den Paketnamen, Versionsnummer, Autor, Lizenz oder Modulabhängigkeiten
beinhalten kann. Eine vollständige Dokumentation zum Aufbau dieser Definitionen ist auf
der offiziellen Webseite des npm-Projekts verfügbar [15].
Der MongoDB-Client liegt zum Zeitpunkt der Implementierung in der npm-Registry unter
dem Paketnamen ”mongodb” in der Version 1.4.5 vor. Deshalb wird die benötigte Versionsnummer auf 1.4 gesetzt, was bedeutet, dass die jeweils höchste verfügbare Version, die mit
1.4 beginnt, installiert wird. Außerdem wird für die Ausführung von Unit-Tests das Modul ”nodeunit” installiert, welches in der Version 0.9.0 verfügbar ist. Da die Unit-Tests nur
während der Entwicklung, aber nicht für die eigentliche Verwendung des Clients benötigt
werden, kann das entsprechende Modul im Abschnitt devDependencies der JSON-Datei angegeben werden.
Die daraus resultierende Konfiguration ist in Listing 6.1 dargestellt und kann nun verwendet
werden, um die Module zu installieren. Hierzu genügt es, den Befehl npm install auf der
39
6. Implementierung eines Prototypen für die Sichtenverwaltung in MongoDB
Kommandozeile im Hauptverzeichnis des Projekts auszuführen.
Listing 6.1: Client-Modulkonfiguration (package.json)
1
{
" name " : " mongodb - views - client " ,
" version " : "0.1.0 - dev " ,
" description " : " Mongo DB client with support for views " ,
" main " : " index . js " ,
" scripts " : {
" test " : " nodeunit tests / db . js "
},
" keywords " : [ " mongodb " , " views " , " client " ] ,
" author " : {
" name " : " Norman Soetbeer " ,
" email " : " norman . soetbeer@uni - rostock . de "
},
" license " : " MIT " ,
" dependencies " : {
" mongodb " : "~1.4"
},
" devDependencies " : {
" nodeunit " : "~0.9"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
}
6.1.2. Erweiterung durch Prototyping
Im Gegensatz zu vielen anderen objektorientierten Programmiersprachen erfolgt die Vererbung in Javascript nicht durch klassische Vererbung, sondern durch prototypische. Dabei
werden Attribute und Methoden erst zur Laufzeit durch das Klonen anderer Objekte vererbt.
Weiterhin können diese Eigenschaften auch zur Laufzeit überschrieben oder sogar wieder vom
Objekt entfernt werden. Jedes Objekt kann wieder als Prototyp zur Erstellung neuer Objekte
dienen.
Um den MongoDB-Client wie in Abschnitt 5.6.1 beschrieben um die notwendigen Methoden zur Sichtverwaltung zu erweitern, wird die vorhandene Datenbankkomponente erweitert.
Diese stellt Methoden zum Verbindungsaufbau, zur Authentifizierung, zur Verwaltung von
Collections und Indizes, sowie zur Benutzerverwaltung bereit. Nun werden die Implementierungen an das Db-Objekt gebunden, wie im Listing 6.2 ansatzweise abgebildet.
Listing 6.2: Prototyping am Db-Objekt
1
2
// Db - Objekt des nativen MongoDB - Clients holen
var Db = require ( ’ mongodb ’) . Db ;
3
4
5
// Funktionen zur Sic htenv erwalt ung hinzuf ü gen
Db . prototype . createView = function ( name , collection , query ,
isMaterialized , callback ) {
40
6.2. Middleware
// ...
6
7
};
8
9
10
11
Db . prototype . deleteView = function ( name , callback ) {
// ...
};
12
13
14
// Modifiziertes Db - Objekt zur Verf ü gung stellen
module . exports = Db ;
Damit dieses manipulierte Db-Objekt auch verwendet wird, kann im Modul-Einstiegspunkt
eine Instanz des nativen Clients geladen und darin das Db-Objekt überschrieben werden, was
in Listing 6.3 dargestellt ist.
Listing 6.3: Modul-Einstiegspunkt des erweiterten Clients
1
2
// Instanz des nativen MongoDB - Clients holen
var MongoClient = require ( ’ mongodb ’) . MongoClient ;
3
4
5
// Db - Objekt mit eigener , erweiterter Implementierung ü berschreiben
MongoClient . Db = require ( ’ ./ db ’) ;
6
7
module . exports = MongoClient ;
Für die Sichtanfragen wird die Collection-Komponente des MongoDB-Clients als Basis
verwendet. Eine Anfrage auf eine Sicht entspricht im Grunde einer Anfrage auf eine Collection
mit dem Präfix ”VIEW:”. Die neu eingeführte View-Komponente setzt also lediglich dieses
Präfix und delegiert alle weiteren Aufrufe weiter an die Collection-Komponente.
6.2. Middleware
Die Middleware besteht aus einem dauerhaft laufenden Prozess, welcher in der Lage sein soll,
Verbindungen von MongoDB-Clients anzunehmen und eine Verbindung zu einem MongoDBServer aufzubauen. Zur Kommunikation zwischen Client, Middleware und Server kommt stets
das MongoDB Wire Protocol [11] zum Einsatz.
Nachfolgend werden die einzelnen Schritte der Implementierung beschrieben, um diese
Middleware zu entwickeln.
6.2.1. Proxyserver
Zunächst wird ein Proxyserver (Abb. 6.1) implementiert, welcher eingehende TCP-Verbindungen
von MongoDB-Clients entgegennimmt. TCP-Verbindungen werden in NodeJS, ähnlich wie in
anderen Sprachen auch, durch sogenannte Sockets abstrahiert. Es können Daten aus SocketVerbindungen gelesen oder in sie hinein geschrieben werden.
In einem ersten Schritt wird für jede eingehende Client-Verbindung eine neue Verbindung zum Zielserver aufgebaut und sämtliche Netzwerkdaten an die jeweils andere SocketVerbindung weitergesendet. Dies wird mittels der pipe-Methode umgesetzt. Diese verwendet
die Ausgabe der ersten Verbindung als Eingabe für die zweite. Die - zwischen Client und
41
6. Implementierung eines Prototypen für die Sichtenverwaltung in MongoDB
Abbildung 6.1.: Datenfluss innerhalb der Middleware (Proxyserver)
Server ausgetauschten - Daten können zum Debuggen auf der Konsole ausgegeben werden,
sind jedoch weiterhin über das MongoDB Wire Protocol [11] codiert.
6.2.2. Wire Protocol
Um diese Datenpakete zu decodieren, findet das MongoDB Wire Protocol [11] Verwendung.
In diesem ist beschrieben, wie die Nachrichtensequenzen (de-)serialisiert werden. Dabei ist
der Header immer gleich aufgebaut, während sich die darauffolgenden, codierten Nutzdaten
von Operation zu Operation unterscheiden.
Listing 6.4: MongoDB Wire Protocol: Nachrichten-Header
1
2
3
4
5
6
7
struct MsgHeader {
int32
messageLength ; // Nachrichtenl ä nge , inkl . Header
int32
requestID ;
// Nachrichten - ID
int32
responseTo ;
// ID der urspr ü nglichen Nachricht
// ( nur f ü r Antworten , sonst 0)
int32
opCode ;
// Operations - Code ( Query , Insert , etc .)
}
Das Protokoll sieht die Verwendung von sieben verschiedenen Operationen für den Client
vor:
• OP QUERY: allgemeine Query-Anfragen, kann aber auch für Insert, Update, Delete verwendet werden
• OP UPDATE: Änderung vorhandener Dokumente
• OP INSERT: Einfügen von Dokumenten
• OP DELETE: Löschen von Dokumenten
• OP GET MORE: Abfrage von Dokumenten anhand eines Cursors aus vorheriger QueryAnfrage
• OP KILL CURSORS: Löschen eines Cursors aus vorheriger Anfrage
• OP MSG: Diagnose-Nachrichten (veraltet)
42
6.2. Middleware
Für den Server hingegen ist lediglich eine Operation vorgesehen, OP REPLY, welche für
sämtliche Antworten verwendet wird. Eine vollständige Liste der Datenstrukturen für diese
Nachrichten befindet sich im Anhang A.
Für die Decodierung einer Nachricht des Clients werden zunächst jeweils 4 Bytes für die
Nachrichtenlänge, Nachrichten-ID, ursprüngliche Nachrichten-ID und den Operations-Code
ausgelesen und anschließend in den entsprechenden Ganzzahlwert (32-Bit Integer) umgewandelt. Danach wird der Operationscode ausgewertet, der z.B. bei einem Query den Wert 2004
enthält. Es folgt das Auslesen der restlichen Datenstruktur. Zur Veranschaulichung wird diese
für ein Query in Listing 6.5 dargestellt.
Listing 6.5: MongoDB Wire Protocol: Query
1
2
3
4
5
6
7
8
9
struct OP_QUERY {
MsgHeader header ;
//
int32
flags ;
//
cstring
fu llC ol le ct io nN am e ; //
int32
numberToSkip ;
//
int32
numberToReturn ;
//
document query ;
//
[ document r e t u r n F i e l d s S e l e c t o r ; ]
}
Nachrichten - Header
Query - Optionen
Name der Collection
Offset
Max . Anzahl an Dokumenten
Anfrage - Objekt , meist Selektion
// Objekt f ü r Projektion
Zeichenketten, wie der Name der angefragten Collection, liegen als UTF-8 codierte, NullByte-terminierte Zeichenketten vor. Bei den Dokumenten handelt es sich um BSON-codierte
Objekte. BSON (Binary JSON) ist eine Codierung, die auf JSON (JavaScript Object Notation)
basiert, aber insbesondere größere, verschachtelte Dokumente Speicher-effizienter serialisieren
kann.
6.2.3. Interceptor
Innerhalb der Middleware gibt es zwei sogenannte Interceptoren. Deren Aufgabe ist es, die einund ausgehenden Nachrichten zu decodieren und im Sinne der Sichtenlogik zu verändern. Sie
stehen miteinander in Verbindung. Dadurch ist es möglich, Nachrichten mit einem Interceptor
zu generieren und die dazugehörige Antwort mit dem anderen abzufangen. Abbildung 6.2
verdeutlicht den Nachrichtenfluss.
Abbildung 6.2.: Datenfluss innerhalb der Middleware mit Interceptoren
Implementiert wurden diese Interceptoren als sogenannte Stream-Transformer. Ein StreamTransformer kann in NodeJS zwischen je zwei Streams, und somit auch zwischen Socket-
43
6. Implementierung eines Prototypen für die Sichtenverwaltung in MongoDB
Verbindungen, geschaltet werden und Datenpakete zurückhalten, verändern oder sogar neue
Datenpakete erzeugen. Damit ist es also möglich, Client-Anfragen umzuschreiben oder über
die bereits hergestellte Verbindung neue Anfragen an die Datenbank zu schicken. Die Antworten auf selbst erzeugte Anfragen können auf dem Rückweg wiederum abgefangen werden. Der
Client erfährt somit nichts über die zusätzlichen Operationen, welche über die Middleware
erzeugt wurden.
6.2.4. Abfangen von Sichten-Operationen
Mit Hilfe des Interceptors aus Abschnitt 6.2.3 sollen Anfragen des Clients nun so transfomiert werden, dass materialisierte Sichten gemäß des Konzepts aus Abschnitt 5.3 automatisch angelegt bzw. gelöscht werden. Anfragen auf virtuelle Sichten sollen, wie in Abschnitt 5.4
vorgestellt, umgeschrieben werden. Nachfolgend werden die hierzu notwendigen Operationen
genauer beschrieben.
Anlegen einer Sicht
Wird eine neue Sichtdefinition durch den Client angelegt, so entspricht dies einer EinfügeOperation in die Collection ”view.definitions”. Das einzufügende Dokument stellt hierbei die
Sichtdefinition selbst dar (Abb. 6.6). Die Sichtdefinition beinhaltet den Namen der Sicht, die
Basiscollection, die Anfrage selbst und die Information, ob die Sicht materialisiert werden
soll.
Listing 6.6: Dokument einer Sichtdefinition
1
{
" name " : " benutzer_v1 " ,
" collection " : " benutzer " ,
" query " : {
" $project " : {
" name " : {
" $ifNull " : [
" $name " ,
{
" $concat " : [ " $vorname " , " " , " $nachname
"]
}
]
},
" geburtstag " : 1 ,
" version " : { " $literal " : 1 }
}
},
" isMaterialized " : true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
}
Das Anlegen der Sicht erfolgt bereits durch das Einfügen dieses Dokuments. Plausibilitätsprüfen bzgl. der Struktur der Anfrage, sowie Prüfungen, ob die Basiscollection existiert,
44
6.2. Middleware
werden im Rahmen dieses Prototyps nicht vorgenommen. Jedoch muss noch zwischen einer
virtuellen und einer materialisierten Sicht unterschieden werden. Für eine materialisierte Sicht
muss die dazugehörige Collection, in welcher das Anfrage-Ergebnis enthalten ist, angelegt und
befüllt werden. Der Stream-Transformer lässt hierzu die ursprüngliche Einfüge-Operation
unverändert passieren und generiert anschließend eine Query-Operation (Abb. 6.3). Genauer
handelt es sich hierbei um eine aggregate-Anfrage, welche die Anfrage aus der Sichtdefinition
enthält. Das Ergebnis wird in der Collection für die materialisierte Sicht gespeichert. In dem
Beispiel 6.6 heißt die dazugehörige Collection ”views.materialized.benutzer v1”. Zukünftige
Anfragen auf die View werden auf diese Collection umgeschrieben.
Abbildung 6.3.: Datenfluss beim Anlegen einer materialisierten Sicht
Außerdem muss der zweite Stream-Transformer, welcher die Server-Nachrichten auswertet,
die Antwort auf die aggregate-Anfrage abfangen. Um diese Antwort zu identifizieren wird
die RequestID aus der Anfrage verwendet, welche später mit der responseTo-ID der Antwort
übereinstimmt.
Bei einer virtuellen Sicht muss keine Anfrage generiert werden. Hier wird lediglich die
Einfüge-Operation unverändert an den Server gesendet.
Löschen einer Sicht
Analog zum Anlegen einer Sicht, entspricht das Löschen einer Sicht dem Löschen des Dokuments aus der Collection ”views.definitions”, welches die jeweilige Sicht beschreibt. Wird
eine Sichtdefinition gelöscht, so muss auch ggf. die dazugehörige materialisierte Sicht gelöscht
werden.
Hierzu wird zunächst die Nachricht zum Löschen des Dokuments zurückgehalten. Anschließend wird ein Query an die Datenbank gesendet, um die Sichtdefinition anzufragen. Aus dieser Sichtdefinition kann nun der Name der Basiscollection entnommen werden und im Falle,
dass es sich um eine materialisierte Sicht handelt, eine weitere Nachricht erzeugt werden,
um die Collection der materialisierten Sicht zu löschen. Außerdem wird die zurückgehaltene
Lösch-Operation des Clients an den Server gesendet.
Updaten einer Sicht
Soll eine vorhandene Sichtdefinition verändert werden, so geschieht dies durch eine UpdateOperation auf dem dazugehörigen Dokument in der Collection der Sichtdefinitionen.
45
6. Implementierung eines Prototypen für die Sichtenverwaltung in MongoDB
Eine Update-Operation kann hierbei wie eine Lösch- und eine anschließende EinfügeOperation betrachtet werden. Dies bedeutet, dass der Client-Befehl zum Updaten des Dokuments zunächst zurückgehalten wird. Anschließend wird die alte Sichtdefinition geladen und
ggf. die bisherige materialisierte Sicht bzw. die dafür zuständige Collection gelöscht. Handelt es sich bei der neuen Sichtdefinition um eine materialisierte Sicht, so wird wieder eine
Aggregate-Anfrage an die Datenbank gesendet, um die entsprechende Collection mit dem
Anfrageergebnis zu erzeugen. Sind all diese Schritte durchlaufen, wird der zurückgehaltene
Client-Befehl weiter an die Datenbank geschickt.
Sicht-Anfragen
Es werden Anfragen der Query-API, sowie der Aggregation-API abgefangen, sofern die ZielCollection mit dem Präfix ”VIEW:” beginnt.
Zunächst wird dabei die Anfrage des Clients zurückgehalten und anschließend die Definition
der entsprechenden Sicht mit Hilfe einer separaten Query-Operation geladen. Abhängig von
der Art der Sicht werden weitere Operationen erzeugt.
Anfrage auf eine materialisierte Sicht Abbildung 6.4 zeigt das Sequenzdiagramm für eine
Anfrage auf eine materialisierte Sicht. Dabei wird der Name der Collection aus der ClientAnfrage (”benutzer”) gegen die Collection der materialisierten Sicht (”views.materialized.benutzer v1”)
ausgetauscht. Nun kann diese Anfrage an den Server weitergeschickt werden. Die ServerAntwort ist mit der Client-Anfrage kompatibel und muss daher nicht abgefangen werden.
Abbildung 6.4.: Sequenzdiagramm für die Anfrage auf eine materialisierte Sicht
46
6.2. Middleware
Anfrage auf eine virtuelle Sicht Das Sequenzdiagramm für die Anfrage auf eine virtuelle
Sicht (Abb. 6.5) hingegen zeigt auf, dass die Anfrage an den Server durch eine AggregateOperation ersetzt wird. Diese wird mit der Request-ID aus der ursprünglichen Nachricht
versehen. Dadurch ist es dem Client möglich, die Serverantwort der vorher gestellten Anfrage
zuzuordnen. Es werden zwei Pipelines in die Anfrage eingebunden: zuerst die der Sichtdefinition und anschließend die der ursprünglichen Anfrage. Das Ergebnis der AggregationOperation ist kompatibel mit dem einer Query-Operation. Somit kann die Antwort an den
Client durchgereicht werden.
Abbildung 6.5.: Sequenzdiagramm für die Anfrage auf eine virtuelle Sicht
47
7. Evaluierung
Nachfolgend werden die Konzepte und Implementierungen dieser Arbeit ausgewertet. Hierzu
werden Übersichten zu angestrebten und erreichten Zielen erstellt. Es wird mit Hilfe der
Anforderungen des Beispiels aus Kapitel 2 bewertet, inwiefern diese Ansätze zur Lösung der
Ausgangsprobleme beitragen.
7.1. Bewertung der Sichtdefinitionen
Die Sichtdefinitionen erlauben die Formulierung der in Abschnitt 4.1 beschriebenen Szenarien
und erfüllen somit die Anforderungen. Diese wurden mit Hilfe von Integrationstests in der
Middleware getestet.
7.2. Performance-Analyse
Die Middleware stellt eine zusätzliche Kommunikationsschicht zwischen Client und Server
dar. Jede zwischengeschaltete Schicht bringt gewisse Performanceeinbußen mit sich, wie z.B.
erhöhte Netzwerklatenzen. In diesem Abschnitt wird versucht, solche Performanceverluste
aufzuführen und die Auswirkungen einzuordnen.
7.2.1. Datenbankoperationen
Durch die Speicherung der Sichtdefinitionen außerhalb der Middleware sind zusätzliche Datenbankanfragen notwendig. Die nachfolgende Übersicht (Tab. 7.1) zeigt auf, wie viele solcher
Anfragen für die jeweiligen Sichtoperationen notwendig sind.
Das Anlegen einer neuen Sicht ist meist nur dann erforderlich, wenn neue Schemaversionen
eingeführt oder neue Anwendungen in ein bestehendes System integriert werden. Zusammen
mit dem Löschen einer Sichtdefinition stellt dies eine eher selten ausgeführte Aufgabe dar.
Zusätzliche Anfragen durch die Middleware sind im Zusammenhang mit diesen Operationen
zu vernachlässigen.
Anders sieht es bei den Anfragen auf virtuelle und materialisierte Sichten aus. Hier sind
spürbare Performanceeinbußen zu erwarten. Zum einen wird der Datenbankserver durch die
vielen zusätzlichen Anfragen einer größeren Belastung ausgesetzt. Zum anderen erhöhen sich
die Antwortzeiten deutlich, da sämtliche Anfragen durch die Middleware nacheinander ausgeführt werden.
7.2.2. Ansätze zur Performanceverbesserung
Da die Sichtdefinitionen nur selten verändert werden, kann eine Verbesserung der Performance durch den Einsatz eines Caches erzielt werden. Somit müssten die Definitionen nur
49
7. Evaluierung
Tabelle 7.1.: Bewertung zusätzlicher Anfragen für Sichtoperationen
Sichtoperation
zusätzliche Anfragen
Bewertung
Erstellen einer virtuellen Sicht
keine weitere Operation
Es wird nur die Einfügeoperation ausgeführt.
keine negative Beeinflussung
Erstellen einer materialisierten Sicht
+1 Operation
Es wird eine aggregate-Operation ausgeführt, um die dazugehörige Collection
zu befüllen.
vernachlässigbar,
Operation
da
seltene
Löschen einer virtuellen Sicht
+1 Operation
Die Sichtdefinition muss geladen werden, um festzustellen, welche Sichtart
vorliegt.
vernachlässigbar,
Operation
da
seltene
Löschen einer materialisierten Sicht
+2 Operationen
Die Sichtdefinition muss geladen werden. Die Collection der materialisierten
Sicht wird gelöscht.
vernachlässigbar,
Operation
da
seltene
Anfrage auf virtuelle Sicht
+n Operationen
Mindestens einmal muss die Sichtdefinition geladen werden. Ist die Sicht über
eine weitere Sicht definiert, müssen weitere Definitionen rekursiv geladen werden, bis Zielcollection eine materialisierte Sicht oder ”echte” Collection ist.
häufige Operation, kann sich negativ auf die Performance auswirken, insbesondere bei Sichten
auf Sichten
Anfrage auf materialisierte Sicht
+1 Operation
Die Sichtdefinition muss geladen werden. Danach wird die Client-Anfrage
nur umgeschrieben.
häufige Operation, kann sich negativ auf die Performance auswirken
jeweils einmal vom Datenbankserver angefordert werden. Mit dem Einfügen oder Löschen einer Sichtbeschreibung muss der dazugehörige Cache-Eintrag invalidiert werden. Dieser Ansatz
ist geeignet, wenn nur eine Instanz der Middleware betrieben wird, weil nur so gewährleistet
werden kann, dass die Cache-Einträge mit den tatsächlichen Definitionen übereinstimmen.
Ein weiterer Lösungsansatz besteht darin, die Sichtdefinitionen durch die Middleware persistent abspeichern zu lassen. Mehrere Middleware-Instanzen könnten mit Hilfe eines internen
Protokolls Änderungen auf Sichtdefinitionen untereinander bekannt machen. Invalide CacheEinträge, wie sie im vorherigen Ansatz vorkommen können, werden somit vermieden. Durch
den Einsatz mehrerer Middleware-Prozesse kann eine horizontale Skalierbarkeit erreicht werden.
50
7.3. Unit- und Integrationstests
7.3. Unit- und Integrationstests
Um die Middleware auf korrekte Implementierung zu prüfen, wurden verschiedene Tests entwickelt.
7.3.1. Unit-Tests
Um die funktionale Korrektheit der Middleware zu gewährleisten, wurden einige Komponenten mit Hilfe von Unit-Tests getestet. Hierzu zählt die BSON-Erweiterung zum Codieren und
Decodieren von Objekten innerhalb von Nachrichten. Mit Hilfe dieser Tests war es möglich,
Fehler schnell zu erkennen und zu beheben.
7.3.2. Integrationstests
Integrationstests gestatten, das Zusammenspiel mehrerer Komponenten automatisiert zu testen. Diese wurden ebenfalls verwendet, um das Erstellen und Löschen von Sichtdefinitionen
und Anfragen auf Sichten zu prüfen. In Listing 7.1 ist solch ein Testfall dargestellt.
Dort wird eine virtuelle Sicht mit einer Selektion angelegt und später eine Anfrage an diese
Sicht gestellt. Nicht hier dargestellt sind die Fixtures, also Dummy-Dokumente, die vorher in
die Basiscollection eingefügt wurden. Nach der Anfrage auf die Sicht werden die erwarteten
Ergebnisse mit den tatsächlichen verglichen.
Listing 7.1: Auszug eines Integrationstests
1
describe ( ’ Virtual views ’ , function () {
2
3
4
5
6
7
8
9
10
11
it ( ’ Create a virtual view ’ , function ( done ) {
var definition = {
name : ’ base_v1_virtual ’ ,
collection : ’ base ’ ,
query : {
$project : { version : 2}
},
isMaterialized : false
};
12
definitions . insert ( definition , function ( err , result ) {
assert . ifError ( err ) ;
assert . ok ( result , ’ The definition should be inserted ’)
;
done () ;
}) ;
13
14
15
16
17
18
}) ;
19
20
21
22
23
it ( ’ Query a virtual view using find () w / o query ’ , function (
done ) {
db . collection ( ’ VIEW : base_v1_virtual ’) . find () . toArray (
function ( err , documents ) {
assert . ifError ( err ) ;
assert . equal (2 , documents . length ) ;
51
7. Evaluierung
done () ;
24
}) ;
25
}) ;
26
27
28
}) ;
7.4. Homogenisierung von Dokumenten
Die Hauptziele dieser Arbeit lagen darin, heterogene Daten zu homogenisieren und verschiedene Repräsentationen von gleichen Basisdaten zu mit Hilfe von Sichten zu erzeugen. Die
Konzeptionen haben mit Beispielen verdeutlicht, dass die Übertragung von Sichtkonzepten
aus relationalen Datenbanken auf NoSQL-Datenbanken möglich ist.
Mit Integrationstests für den Prototypen wurden einige dieser Szenarien nachgebildet und
somit auch die praktische Machbarkeit nachgewiesen.
Der praktische Nutzen dieser Möglichkeiten wurde durch ein begleitendes Beispiel einer
Webarchitektur dargelegt.
52
8. Zusammenfassung
In dieser Diplomarbeit wurde untersucht, inwiefern die Übertragung von Sichtkonzepten aus
dem Bereich der SQL-Datenbanken auf NoSQL-Datenbanken möglich ist. Die Hauptziele waren hierbei, Heterogenitäten zu beseitigen und verschiedene Repräsentationen von Basisdaten
zu ermöglichen.
Nach einer Erfassung der Anforderungen konnte im Kapitel ?? der Aufbau von Sichtdefinitionen beschrieben werden. Mit Hilfe einer Middleware ist es möglich, diese Sichtdefinitionen
auf einem MongoDB-Server zu verwalten. Basierend auf diesen Sichtbeschreibungen können
Anfrageergebnisse so transformiert werden, dass sie homogene Strukturen aufweisen. Die dazu
notwendigen Anfragetransformationen wurden ebenfalls über diese Middleware realisiert.
Mit dem Erreichen der Hauptziele wurden die Sichtkonzepte erfolgreich auf eine NoSQLDatenbank übertragen.
8.1. Aussichten und Erweiterbarkeit
Der, in dieser Arbeit entwickelte, Middleware-Prototyp wurde mit der Intention erstellt, Sichtenkonzepte in NoSQL-Datenbanken umzusetzen. Für die Verwendung in einer Produktivumgebung müssten Performanceoptimierungen vorgenommen werden, welche möglicherweise
Änderungen an der Architektur erfordern. Ansätze hierzu wurden im Abschnitt 7.2.1 vorgestellt.
Da die Sichttransformation nicht im Client stattfindet (vgl. Abschnitt 5.7), ist eine Autorisierung für einzelne Sichten denkbar, ähnlich wie es sie auch für SQL-Datenbanken gibt.
Somit ließe sich der Zugriff auf eine Teilmenge der Attribute oder Datensätze für Benutzer
und Anwendungen einschränken.
Die Client-Middleware-Server-Architektur wurde u.a. für die Implementierung ausgewählt,
weil es dadurch einfacher ist, Clients in anderen Programmiersprachen zu entwickeln. Dies
bietet die Möglichkeit, die Sichtenverwaltung auch in Webanwendungen zu nutzen, die bspw.
in PHP oder Java entwickelt werden.
53
A. MongoDB Wire Protocol
1
2
Datentypen
----------
3
4
5
6
7
int32 : vorzeichenlose 32 - bit Ganzzahl , " Little Endian " codiert
int64 : vorzeichenlose 64 - bit Ganzzahl , " Little Endian " codiert
cstring : UTF -8 codierte Zeichenkette , Null - Byte terminiert
document : BSON codiertes Objekt
8
9
10
11
Nachrichtenkopf
---------------
12
13
14
15
16
17
18
19
struct MsgHeader {
int32
messageLength ; // Nachrichtenlänge , inkl . Header
int32
requestID ;
// Nachrichten - ID
int32
responseTo ;
// ID der ursprünglichen Nachricht
// ( nur für Antworten , sonst 0)
int32
opCode ;
// Operations - Code ( Query , Insert , etc .)
}
20
21
22
23
Anfrage - Operation
-----------------
24
25
26
27
28
29
30
31
32
33
struct OP_QUERY {
MsgHeader header ;
int32
flags ;
cstring
fu llC ol le ct io nN am e ;
int32
numberToSkip ;
int32
numberToReturn ;
document query ;
[ document r e t u r n F i e l d s S e l e c t o r ; ]
}
//
//
//
//
//
//
//
Nachrichten - Header
Query - Optionen
Name der Collection
Offset
Max . Anzahl an Dokumenten
Anfrage - Objekt , meist Selektion
Objekt für Projektion
34
35
36
37
Update - Operation
----------------
38
39
40
41
42
43
struct OP_UPDATE {
MsgHeader header ;
int32
ZERO ;
cstring
fu llC ol le ct io nN am e ;
int32
flags ;
//
//
//
//
Nachrichten - Header
für zukünftige Nutzung reserviert
Name der Collection
Update - Optionen
55
A. MongoDB Wire Protocol
document
document
44
45
selector ;
update ;
46
47
// Selektor - Objekt
// Beschreibung , welche Attribute wie
// verändert werden sollen
}
48
49
50
51
Einfüge - Operation
-----------------
52
53
54
55
56
57
58
struct OP_INSERT {
MsgHeader header ;
int32
flags ;
cstring
fu ll Co lle ct io nN am e ;
document * documents ;
}
//
//
//
//
Nachrichten - Header
Einfüge - Optionen
Name der Collection
ein oder mehrere Dokumente
//
//
//
//
//
Nachrichten - Header
für zukünftige Nutzung reserviert
Name der Collection
Lösch - Optionen
Selektor für zu löschende Dokumente
//
//
//
//
//
Nachrichten - Header
für zukünftige Nutzung reserviert
Name der Collection
max . Anzahl an Dokumenten ( Limit )
Cursor - ID aus vorheriger Anfrage
59
60
61
62
Lösch - Operation
----------------
63
64
65
66
67
68
69
70
struct OP_DELETE {
MsgHeader header ;
int32
ZERO ;
cstring
fu llC ol le ct io nN am e ;
int32
flags ;
document selector ;
}
71
72
73
74
Nachlade - Operation
------------------
75
76
77
78
79
80
81
82
struct OP_GET_MORE {
MsgHeader header ;
int32
ZERO ;
cstring
fu llC ol le ct io nN am e ;
int32
numberToReturn ;
int64
cursorID ;
}
83
84
85
86
Cursor - Lösch - Operation
----------------------
87
88
89
90
91
92
93
struct OP_KILL_CURSOR {
MsgHeader header ;
int32
ZERO ;
int32
numbe rOfCur sorIDs ;
int64 *
cursorIDs ;
}
94
56
//
//
//
//
Nachrichten - Header
für zukünftige Nutzung reserviert
Anzahl der zu löschenden Cursor
Sequenz mit zu löschenden Cursor - IDs
95
96
97
Diagnose - Operation
------------------
98
99
100
101
102
struct OP_MSG {
MsgHeader header ; // Nachrichten - Header
cstring
message ; // Diagnose - Nachricht
}
103
104
105
106
Antwort - Operation
-----------------
107
108
109
110
111
112
113
114
115
struct OP_REPLY {
MsgHeader header ;
int32
responseFlags ;
int64
cursorID ;
int32
startingFrom ;
int32
numberReturned ;
document * documents ;
}
//
//
//
//
//
//
Nachrichten - Header
diverse Flags , u . a . für Fehler
Cursor - ID für Nachlade - Operation
Offset des aktuellen Cursors
Anzahl der Ergebnis - Dokumente
Ergebnis - Dokument ( e )
57
B. Installation und Nutzung der Middleware
Um die Middleware testen zu können, muss folgende Software auf dem System installiert sein:
• Node.js (Version 0.10 oder neuer)
• MongoDB (Version 2.2 oder neuer)
• npm (Node.js Paketmanager, ist in der Installation von Node.js enthalten)
Die Installationspakete können auf den Webseiten von Node.js (http://nodejs.org/) und
MongoDB (http://www.mongodb.org/) heruntergeladen werden.
Die Middleware wurde unter OS X 10.9 entwickelt und getestet, sollte jedoch auch unter
Linux oder Windows lauffähig sein.
Beide Projekte, Client und Middleware, sind ähnlich strukturiert. Im Hauptverzeichnis
befinden sich diverse Konfigurationsdateien, die Einstellungen für IDEs (Integrated Development Environment) enthalten. Erwähnenswerte Dateien sind hier die packages.json (npm
Paketbeschreibung) und die index.js, welche den Einstiegspunkt für die Projekte darstellen.
Im Verzeichnis lib befindet sich jeweils die Implementierung für den Client bzw. die Middleware. Im test-Verzeichnis liegen die Unit- und Integrationstests. Unterhalb von node modules
liegen externe Bibliotheken wie Unit-Test-Frameworks oder der ”MongoDB Native Driver”,
die über den Paketmanager installiert wurden.
Die Middleware ist so konfiguriert, dass sie Verbindungen auf dem Port 27018 entgegennimmt und Verbindungen zum MongoDB-Server auf Port 27017 aufbaut. Sie kann auf der
Kommandozeile über den Befehl npm start gestartet werden. Zum Beenden kann die Tastenkombination STRG+C gedrückt werden. Die Tests können mit dem Befehl npm test
ausgeführt werden.
Für die Ausführung der Tests ist es nicht notwendig, die Middleware vorher zu starten.
Sie wird intern automatisch zu Beginn der Tests gestartet und nach dem Durchlauf wieder
beendet.
59
Literatur
[1] Apache CouchDB. url: http://couchdb.apache.org/.
[2] Apache HBase. url: http://hbase.apache.org/.
[3] BSON - Binary JSON. url: http://bsonspec.org/.
[4]
Fay Chang u. a. Bigtable: A Distributed Storage System for Structured Data Bigtable:
A distributed storage system for structured data. Google, 2006.
[5]
Jeff Dean und Sanjay Ghemawat. MapReduce: Simplified Data Processing on Large
Clusters. 2004. url: http://research.google.com/archive/mapreduce.html.
[6] Git Repository ”Mongo DB Native NodeJS Driver” auf GitHub. url: https://github.
com/mongodb/node-mongodb-native.
[7] Hypertable. url: http://hypertable.com/.
[8] Introduction Into The Views - Apache CouchDB 1.7.0 Documentation. url: http :
//docs.couchdb.org/en/latest/couchapp/views/intro.html.
[9] MongoDB. url: http://www.mongodb.org/.
[10] MongoDB Aggregation Pipeline. url: http : / / docs . mongodb . org / manual / core /
aggregation-pipeline/.
[11]
Inc. MongoDB. MongoDB Wire Protocol. url: http : / / docs . mongodb . org / meta driver/latest/legacy/mongodb-wire-protocol/ (besucht am 09. 04. 2014).
[12] Node.js. url: http://nodejs.org/.
[13] NoSQL Databases. url: http://nosql-database.org/.
[14] npm (Node Package Modules). url: https://www.npmjs.org/.
[15] npm package.json Dokumentation. url: https://www.npmjs.org/doc/json.html.
[16]
Till Rausch. Service Orientierte Architektur: Übersicht und Einordnung. url: http:
//web.archive.org/web/20081010033719/http://www.till-rausch.de/assets/
baxml/soa_akt.pdf (besucht am 10. 10. 2008).
[17]
Gunter Saake, Andreas Heuer und Kai-Uwe Sattler. Datenbanken: Implementierungstechniken. Bd. 2. mitp-Verlag, Bonn, 2005.
[18]
Stefani Scherzinger, Meike Klettke und Uta Störl. Managing Schema Evolution in NoS”
QL Data Stores Managing Schema Evolution in NoSQL Data Stores“. In: Proceedings
of the 14th International Symposium on Database Programming Proceedings of the 14th
International Symposium on Database Programming Languages (DBPL 2013), August
30, 2013, Riva del Garda, Trento, Italy. 2013.
[19] The Apache Cassandra Project. url: http://cassandra.apache.org/.
61
Literatur
[20] The JSON Data Interchange Format. url: http://www.ecma-international.org/
publications/files/ECMA-ST/ECMA-404.pdf.
62
Abbildungsverzeichnis
2.1
Architektur und Datenfluss einer Webanwendung . . . . . . . . . . . . . . . .
12
5.1
5.2
Architektur A: Sichtenverwaltung durch Middleware . . . . . . . . . . . . . .
Architektur B: Sichtenverwaltung im Client . . . . . . . . . . . . . . . . . . .
35
36
6.1
6.2
6.3
6.4
6.5
Datenfluss innerhalb der Middleware (Proxyserver) . . . . . . .
Datenfluss innerhalb der Middleware mit Interceptoren . . . . .
Datenfluss beim Anlegen einer materialisierten Sicht . . . . . .
Sequenzdiagramm für die Anfrage auf eine materialisierte Sicht
Sequenzdiagramm für die Anfrage auf eine virtuelle Sicht . . .
42
43
45
46
47
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
63
Tabellenverzeichnis
5.1
Vergleich der Architekturen zur Sichtenverwaltung . . . . . . . . . . . . . . .
36
7.1
Bewertung zusätzlicher Anfragen für Sichtoperationen . . . . . . . . . . . . .
50
65
Glossar
API
Bei einer API (Application Programming Interface) handelt es sich um eine Programmierschnittstelle auf Quelltext-Ebene.. 17, 21
BSON
Bei BSON (Binary JSON) handelt es sich um ein Datenaustauschformat, welches auf
JSON basiert. Durch die binäre Codierung von Datentypen können Dokumente meist
mit weniger Speicherbedarf serialisiert werden. [3]. 43
JSON
JSON (JavaScript Object Notation) ist ein leichtgewichtiges Datenaustauschformat,
welches auf einer Untermenge der Programmiersprache JavaScript basiert. Es ist für
die Serialisierung hierarchischer Strukturen geeignet. [20].. 39, 43, 67
Node.js
Node.js [12] ist eine Plattform, welche auf die für den Browser ”Google Chrome” entwickelte Javascript-Laufzeitumgebung ”V8” aufbaut. Aufgrund ihres Event-basierten,
nicht-blockierenden Eingabe/Ausgabe-Models ist sie besonders für datenintensive Netzwerkanwendungen geeignet.. 3, 67
npm
npm [14] ist der offizielle Paket-Manager für Node.js. Mit ihm lassen sich Node.jsModule, die auf der Plattform npmjs.org registriert sind, automatisch installieren und
updaten. Eventuelle Abhängigkeiten zu anderen Modulen werden automatisch aufgelöst
und ebenfalls installiert. Die Abkürzung ”npm” steht für das rekursive Backronym ”npm
is not an acronym”.. 39
67
Selbständigkeitserklärung
Ich erkläre, dass ich die vorliegende Arbeit selbständig und nur unter Vorlage der angegebenen
Literatur und Hilfsmittel angefertigt habe.
Rostock, den 30. September 2014
Herunterladen