Verteilte Auswertung von RDF-Graphen mit MapReduce und ...
Verteilte Auswertung von RDF-Graphen mit MapReduce und ...
Verteilte Auswertung von RDF-Graphen mit MapReduce und ...
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
Bachelorarbeit<br />
<strong>Verteilte</strong> <strong>Auswertung</strong> <strong>von</strong><br />
<strong>RDF</strong>-<strong>Graphen</strong> <strong>mit</strong> <strong>MapReduce</strong><br />
<strong>und</strong> NoSQL-Datenbanken<br />
Antony R. Neu<br />
26.08.2011<br />
Albert-Ludwigs-Universität Freiburg im Breisgau<br />
Technische Fakultät<br />
Institut für Informatik
“Anyone can build a fast CPU.<br />
The trick is to build a fast system.“<br />
- Seymour Cray
Bearbeitungszeitraum<br />
26. 05. 2011 – 26. 08. 2011<br />
Gutachter<br />
Prof. Dr. Georg Lausen<br />
Betreuer<br />
Alexander Schätzle<br />
Martin Przyjaciel-Zablocki
Kurzfassung<br />
In der vorliegenden Arbeit wird die Speicherung <strong>und</strong> <strong>Auswertung</strong> <strong>von</strong> <strong>RDF</strong>-Daten<br />
<strong>mit</strong> der Kombination aus dem Apache Hadoop Framework <strong>und</strong> der NoSQL-Datenbank<br />
Cassandra untersucht. Cassandra ermöglicht die verteilte Speicherung der Daten<br />
<strong>und</strong> vereint dabei die Eigenschaften <strong>von</strong> Google BigTable <strong>und</strong> Amazon Dynamo. Um<br />
SPARQL Basic Graph Pattern-Anfragen auf den <strong>RDF</strong>-Daten parallel auszuwerten,<br />
wird das quelloffene Projekt Apache Hadoop verwendet. Dabei wird die Komponente<br />
<strong>MapReduce</strong> des Frameworks zur Berechnung der notwendigen Verbünde eingesetzt,<br />
während das verteilte Dateisystem HDFS der Speicherung der Zwischenergebnisse<br />
dient.<br />
Nachdem die Strategien zur <strong>Auswertung</strong> <strong>und</strong> Speicherung vorgestellt wurden, werden<br />
die technischen Details der Implementierung erläutert. Anschließend wird die<br />
Implementierung <strong>mit</strong> synthetisch generierten Daten evaluiert. Die Evaluation liefert<br />
zwar gute Laufzeiten bei der <strong>Auswertung</strong> der Daten, jedoch zeigen sich Probleme<br />
bei der Systemstabilität.<br />
Schlagwörter: <strong>MapReduce</strong>, Hadoop, Cassandra, NoSQL, <strong>RDF</strong> Triple Store, SP 2 Bench,<br />
Map-Side-Join<br />
1
Inhaltsverzeichnis<br />
Kurzfassung 1<br />
1 Einführung 4<br />
2 Gr<strong>und</strong>lagen 6<br />
2.1 Semantic Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6<br />
2.1.1 Resource Description Framework . . . . . . . . . . . . . . . . 6<br />
2.1.2 SPARQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />
2.2 Apache Hadoop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
2.3 Apache Cassandra . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />
2.3.1 Datenmodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />
2.3.2 Verteilung der Daten im Cluster . . . . . . . . . . . . . . . . . 15<br />
2.3.3 Datenzugriff . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />
3 Strategien zur <strong>Auswertung</strong> <strong>und</strong> Speicherung der <strong>RDF</strong>-Daten 18<br />
3.1 Speicherstrategie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
3.2 <strong>Auswertung</strong>sstrategie . . . . . . . . . . . . . . . . . . . . . . . . . . . 22<br />
4 Implementierung <strong>und</strong> Optimierungen 25<br />
4.1 Einlesen der Daten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
4.2 Auswerten der Anfragen . . . . . . . . . . . . . . . . . . . . . . . . . 27<br />
4.2.1 Generieren der Jobs . . . . . . . . . . . . . . . . . . . . . . . . 27<br />
4.2.2 Map-Side Join . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />
4.2.3 Alternativen zum Map-Side Join . . . . . . . . . . . . . . . . 28<br />
4.2.4 Optimierung für Hadoop . . . . . . . . . . . . . . . . . . . . . 30<br />
5 Evaluation 33<br />
5.1 Allgemeine Rahmenbedingunen . . . . . . . . . . . . . . . . . . . . . 33<br />
2
5.2 Laufzeiten <strong>und</strong> Beobachtungen . . . . . . . . . . . . . . . . . . . . . . 34<br />
5.2.1 Q1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34<br />
5.2.2 Q2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />
5.2.3 Q3A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />
5.2.4 Q10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />
5.2.5 Einfluss des Konsistenzgrads . . . . . . . . . . . . . . . . . . . 39<br />
5.2.6 Einfluss des Replikationsfaktors . . . . . . . . . . . . . . . . . 40<br />
5.2.7 Datenverteilung . . . . . . . . . . . . . . . . . . . . . . . . . . 41<br />
5.3 Probleme <strong>und</strong> mögliche Ursachen . . . . . . . . . . . . . . . . . . . . 41<br />
6 Verwandte Arbeiten 43<br />
7 Zusammenfassung 45<br />
7.1 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />
3
1 Einführung<br />
Die Idee des Semantic Web, die im Jahr 2001 <strong>von</strong> Tim Berners-Lee vorgestellt wurde,<br />
ermöglicht Maschinen das automatische Ableiten <strong>von</strong> Wissen aus dem World<br />
Wide Web. Das stichwortbasierte World Wide Web, das man heute größtenteils<br />
vorfindet, verhindert, dass Maschinen die Semantik hinter den Wörtern verstehen.<br />
Das Resource Description Framework (<strong>RDF</strong>) wurde entwickelt, um eine Ressource<br />
<strong>mit</strong> semantischen Annotationen zu versehen [1]. Eine Ressource kann nicht nur eine<br />
Webseite sein, sondern jede Entität, die sich durch eine eindeutige URI (Unique<br />
Resource Identifier) identifizieren lässt.<br />
Die Vision des Semantic Web funktioniert nur dann, wenn es möglich ist, die riesigen<br />
Daten auszuwerten. Die Speicherung <strong>und</strong> Analyse der Datensätze können oft<br />
nicht <strong>von</strong> einem Rechner alleine realisiert werden. Auch Supercomputer bieten keine<br />
Alternative, denn das Semantic Web soll allen, auch kleineren Firmen oder Institutionen<br />
zur Verfügung stehen. Abhilfe schafft das Konzept des Cloud Computing:<br />
Die Berechnungen finden nicht mehr lokal statt, sondern werden an ein skalierbares<br />
Rechnernetz ausgelagert. Firmen wie Amazon <strong>und</strong> Rackspace vermieten solche<br />
Rechnernetze (Clouds), deren Leistung <strong>und</strong> Kosten sich nach der tatsächlichen Rechenlast<br />
richten. Zur Nutzung dieser skalierbaren Systeme bedarf es Anwendungen<br />
<strong>und</strong> Frameworks, die diese Form der Berechnungen unterstützen.<br />
Hadoop <strong>MapReduce</strong> ist eine quelloffene Implementierung des <strong>MapReduce</strong>-Frameworks,<br />
das ursprünglich <strong>von</strong> Google entwickelt worden ist. Es ermöglicht das Speichern<br />
großer Daten auf dem verteilten Dateisystem HDFS (Hadoop Distributed File System)<br />
<strong>und</strong> das Anwenden <strong>von</strong> Programmen auf diesen Daten. Facebook <strong>und</strong> Twitter<br />
setzen diese Technologie ein, um tausende Petabyte an Daten zu verwalten. Die<br />
Daten können nicht nur blockweise auf dem verteilten Dateisystem, sondern auch<br />
strukturiert <strong>mit</strong> Indizes in Datenbanken gespeichert werden. Das Hadoop-Projekt<br />
beinhaltet zu diesem Zweck eine eigene NoSQL-Datenbank HBase, die bereits zur<br />
Speicherung <strong>und</strong> <strong>Auswertung</strong> <strong>von</strong> <strong>RDF</strong>-Daten untersucht worden ist [2]. NoSQL-<br />
4
Einführung<br />
Datenbanken zeichnen sich durch den Verzicht auf Schemata zur besseren horizontalen<br />
Skalierung aus. Cassandra ist eine weitere NoSQL-Datenbank, <strong>mit</strong> der Firmen<br />
wie beispielsweise Facebook, Digg <strong>und</strong> Twitter gute Erfahrungen gemacht haben.<br />
Ziel dieser Arbeit ist es, die Eigenschaften, die sich aus der Kombination <strong>von</strong> Cassandra<br />
<strong>mit</strong> <strong>MapReduce</strong> ergeben, in Hinblick auf die <strong>Auswertung</strong> <strong>von</strong> SPARQL Basic<br />
Graph Patterns auf großen <strong>RDF</strong>-<strong>Graphen</strong> zu untersuchen. Die Arbeit beschränkt<br />
sich auf das Auswerten einer Folge <strong>von</strong> SPARQL Basic Graph Patterns <strong>mit</strong> maximal<br />
zwei Variablen pro Tripel. Hierfür wurde eine Implementierung entwickelt, die die<br />
Besonderheiten des Datenmodells zum schnellen Zugriff auf die Daten ausnutzt.<br />
Die Arbeit ist wie folgt aufgebaut: In Kapitel 2 werden die Gr<strong>und</strong>lagen des Semantic<br />
Webs <strong>und</strong> der verwendeten Technologien vorgestellt. Darauf aufbauend werden im<br />
folgenden Kapitel die allgemeinen Strategien zur <strong>Auswertung</strong> <strong>und</strong> Speicherung der<br />
<strong>RDF</strong>-Daten vorgestellt. Kapitel 4 erläutert wie diese Strategien in der Implementierung<br />
technisch umgesetzt worden sind. Die Ergebnisse der Evaluation werden in<br />
Kapitel 5 vorgestellt. Es wurden sowohl das Verhalten bei verschiedenen Datengrößen,<br />
als auch der Einfluss verschiedener Parameter untersucht. In Kapitel 6 werden<br />
verwandte Arbeiten vorgestellt <strong>und</strong> die Unterschiede aufgezeigt. Abschließend wird<br />
in Kapitel 7 ein Fazit <strong>und</strong> Ausblick gegeben.<br />
5
2 Gr<strong>und</strong>lagen<br />
2.1 Semantic Web<br />
Im Jahr 2001 stellte Tim Berners-Lee, der Direktor des W3C, seine Vision des Semantic<br />
Webs in einem Artikel in Scientific American vor [1]. Das Semantic Web soll<br />
nach Lee nicht das damalige World Wide Web ersetzen, sondern eine Erweiterung<br />
dessen sein. Webseiten sind größtenteils so aufgebaut, dass Menschen sie lesen <strong>und</strong><br />
verstehen können, Maschinen allerdings nicht. Maschinen können nicht erkennen, ob<br />
<strong>mit</strong> dem Wort „Bank“ das Finanzinstitut oder eine Sitzmöglichkeit im Park gemeint<br />
ist. Das Semantic Web erlaubt es Maschinen, Wissen aus Webseiten zu gewinnen<br />
<strong>und</strong> diese zu verknüpfen. In dem Artikel beschreibt Berners-Lee, dass es im Semantic<br />
Web intelligenten Systemen möglich sein wird, Informationen für die Benutzer zu<br />
gewinnen <strong>und</strong> zu verbinden, die sie sonst selbst recherchieren müssten. Dies sei nur<br />
<strong>mit</strong> einem offenen Standard möglich, der jedoch bis heute noch nicht ausreichend<br />
verwendet wird [3]. Seiten wie Wikipedia, Twitter oder Flickr stellen zwar diese Informationen<br />
bereit, da sie aber nicht gemeinsam offene Standards verwenden, können<br />
sie nicht <strong>mit</strong>einander verknüpft werden.<br />
2.1.1 Resource Description Framework<br />
Das Resource Description Framework (<strong>RDF</strong>) ist ein W3C-Standard, der es ermöglicht,<br />
Inhalte <strong>von</strong> Webseiten <strong>mit</strong> ihrer Bedeutung zu verknüpfen [4]. Das Hauptelement<br />
eines <strong>RDF</strong>-Dokuments ist die sogenannte Aussage. Sie besteht aus drei<br />
Elementen: Subjekt, Prädikat <strong>und</strong> Objekt. Das Subjekt ist eine URI (Unique Resource<br />
Identifier), die eindeutig eine Ressource, z.B. eine Person, beschreibt. Das<br />
Prädikat ist ebenfalls eine URI <strong>und</strong> dient der Beschreibung der Relation zwischen<br />
Subjekt <strong>und</strong> Objekt. Beim Objekt handelt es sich entweder um eine URI, wenn eine<br />
Beziehung zwischen zwei Ressourcen hergestellt werden soll, oder um ein Literal,<br />
6
2.1 Semantic Web<br />
zum Beispiel eine Zahl [3]. Sollen Subjekt oder Objekt nicht benannt werden, kann<br />
auch ein sogenannter leerer Knoten anstatt einer URI verwendet werden. Dadurch<br />
lassen sich Beziehungen zu Objekten modellieren, die nicht bekannt sind oder nicht<br />
benannt werden sollen. Betrachtet man die Aussagen als eine vom Subjekt ausgehende<br />
gerichtete Kante zwischen Subjekt <strong>und</strong> Objekt, die <strong>mit</strong> dem Prädikat beschriftet<br />
wird, so ergibt sich ein gerichteter Graph für das <strong>RDF</strong>-Dokument, der als <strong>RDF</strong>-<br />
Graph bezeichnet wird. Abb. 2.1 zeigt ein Beispiel eines solchen <strong>RDF</strong>-<strong>Graphen</strong>, der<br />
Informationen zur Person Eric Miller beschreibt.<br />
Abbildung 2.1: Beispiel eines <strong>RDF</strong>-<strong>Graphen</strong>. Quelle: [5]<br />
Die Person wird dabei eindeutig anhand der URI http://www.w3.org/People/<br />
EM/contact#me identifiziert, sodass es zu keinen Mehrdeutigkeiten kommen kann.<br />
Als Enkodierung wird häufig die XML-Syntax oder die Turtle-Syntax (Terse <strong>RDF</strong><br />
Triple Language 1 ) gewählt, während Letztere leichter für Menschen zu lesen ist <strong>und</strong><br />
im Folgenden verwendet wird. In der Turtle-Syntax werden Aussagen wie Sätze der<br />
natürlichen Sprache <strong>mit</strong> der Reihenfolge Subjekt, Prädikat <strong>und</strong> Objekt geschrieben<br />
<strong>und</strong> <strong>mit</strong> einem Punkt beendet. Zur Abkürzung können für jedes <strong>RDF</strong>-Dokument<br />
Präfixe definiert werden, die automatisch ergänzt werden. Einige Tripel des <strong>RDF</strong>-<br />
<strong>Graphen</strong> aus Abb. 2.1 werden demnach wie in Listing 2.1 dargestellt enkodiert.<br />
1 http://www.w3.org/TeamSubmission/turtle/<br />
7
2.1 Semantic Web<br />
@prefix r d f : .<br />
@prefix contact : .<br />
http : / /www. w3 . org / People /EM/ contact#me r d f : type contact : Person .<br />
http : / /www. w3 . org / People /EM/ contact#me contact : fullName Eric M i l l e r .<br />
http : / /www. w3 . org / People /EM/ contact#me contact : mailbox mailto :em@w3. org .<br />
Listing 2.1: Beispielhaft Enkodierung <strong>von</strong> <strong>RDF</strong>-Daten<br />
Datenspeicher für <strong>RDF</strong>-Tripel werden häufig als Triple-Stores bezeichnet. Die Funktionalität<br />
solcher Stores reicht <strong>von</strong> einfachen Datenspeichern bis hin zu ausgereiften<br />
Datenbanksystemen, die Anfragen auf <strong>RDF</strong>-Daten auswerten können. Vertreter solcher<br />
ausgereifter Triple-Stores sind beispielsweise Sesame 2 <strong>und</strong> Jena 3 .<br />
2.1.2 SPARQL<br />
SPARQL (SPARQL Protocol And <strong>RDF</strong> Query Language) ist eine Anfragesprache<br />
für <strong>RDF</strong>-Daten, ähnlich SQL für relationale Datenbanken. Seit dem 15. Januar 2008<br />
ist SPARQL eine offizielle Empfehlung des W3C (W3C recommendation) [6]. Diese<br />
Einführung soll im Wesentlichen nur den Teil der Syntax abdecken, der auch <strong>von</strong><br />
der Implementierung unterstützt wird. Zum leichteren Verständnis, wird die Syntax<br />
anhand eines Beispiels aus [4] eingeführt.<br />
PREFIX ex <br />
SELECT ?titel ?author<br />
WHERE<br />
{ ?buch ex:VerlegtBei .<br />
?buch ex:Titel ?titel .<br />
?buch ex:Autor ?autor .<br />
}<br />
Listing 2.2: Beispielhafte SPARQL-Anfrage (Quelle: [4])<br />
Die in Listing 2.2 dargestellte Anfrage gibt die Titel <strong>und</strong> Autoren aller Bücher, die<br />
beim Springer-Verlag verlegt wurden, zurück. Wie im Beispiel zu sehen ist, besteht<br />
eine SPARQL-Anfrage zum größten Teil aus <strong>RDF</strong>-Tripeln, die in Turtle-Syntax geschrieben<br />
sind. Die Tripel beschreiben einen Subgraph des <strong>RDF</strong>-<strong>Graphen</strong>, auf den<br />
2 http://www.openrdf.org/<br />
3 http://jena.sourceforge.net/<br />
8
2.1 Semantic Web<br />
man die Anfrage stellt. An jeder Tripelposition (Subjekt, Prädikat oder Objekt) ist<br />
es möglich, eine Variable zu platzieren, die durch ein „?“ gekennzeichnet wird. Eine<br />
Menge solcher Tripel wird als Basic Graph Pattern bezeichnet. Die Formatierung<br />
der Ausgabe erfolgt <strong>mit</strong> dem Befehl „SELECT“ gefolgt <strong>von</strong> einer Teilmenge der<br />
verwendeten Variablen, d. h. es werden nur die Werte der Variablen ausgegeben, die<br />
nach „SELECT“ aufgeführt werden. Wie bei der Deklaration <strong>von</strong> <strong>RDF</strong>-Daten ist es<br />
möglich Präfixe zu definieren, um gekürzte Schreibweisen zu verwenden. SPARQL<br />
erlaubt weitere Befehle wie zum Beispiel „FILTER“, „LIMIT“ <strong>und</strong> „UNION“, um<br />
die Ergebnisse weiter einzuschränken oder zu erweitern.<br />
PREFIX ex <br />
SELECT ?titel ?author<br />
WHERE<br />
{ ?buch ex:Titel ?titel .<br />
?buch ex:Autor ?autor .<br />
{?buch ex:VerlegtBei .}<br />
UNION<br />
{?buch ex:VerlegtBei . }<br />
}<br />
Listing 2.3: Beispielhafte SPARQL-Anfrage <strong>mit</strong> Operatoren<br />
Die Anfrage, die in Listing 2.3 dargestellt ist, selektiert alle Paare aus Titel <strong>und</strong><br />
Autor der Verlage Springer <strong>und</strong> O’Reilly. Diese weiteren Befehle werden im Folgenden<br />
in dieser Arbeit nicht weiter betrachtet, da sie <strong>von</strong> der Implementierung<br />
nicht unterstützt werden. Ebenso erfolgt keine Formatierung durch „SELECT“. Die<br />
Implementierung beschränkt sich auf die <strong>Auswertung</strong> <strong>von</strong> Anfragen, die aus einem<br />
Basic Graph Pattern bestehen. Weiterhin wird angenommen, dass pro Tripel nicht<br />
mehr als zwei Variablen vorkommen. Die Anfragen haben also alle die Form, die in<br />
Listing 2.4 dargestellt wird.<br />
9
2.2 Apache Hadoop<br />
PREFIX p1 <br />
PREFIX ...<br />
SELECT *<br />
WHERE<br />
{ s1 p1 ?v1 .<br />
}<br />
?v1 p2 o2 .<br />
...<br />
Listing 2.4: SPARQL-Anfrage, die <strong>von</strong> der Implementierung unterstützt wird.<br />
2.2 Apache Hadoop<br />
Hadoop ist ein quelloffenes Projekt <strong>mit</strong> vielen Unterprojekten zur Speicherung <strong>und</strong><br />
Analyse großer Daten, das seine Wurzeln im Apache Nutch 4 Projekt hat. Hadoop<br />
wurde ursprünglich <strong>von</strong> Doug Cutting entwickelt <strong>und</strong> basiert auf dem verteilten<br />
Dateisystem Google FS, sowie Google <strong>MapReduce</strong>, einem Framework für nebenläufige<br />
Berechnungen über große Daten. <strong>MapReduce</strong> wurde ebenfalls <strong>von</strong> Google zur<br />
Wartung der Suchindizes entwickelt. Die beiden Technologien wurden 2003 <strong>und</strong> 2004<br />
in [7, 8] vorgestellt. Seit 2006 beteiligt sich Yahoo an dem Projekt <strong>und</strong> entwickelte<br />
zwischenzeitlich seine eigene Distribution. 2008 wurde das Projekt zu einem offiziellen<br />
Apache Top-Level-Projekt <strong>und</strong> wurde seitdem <strong>von</strong> Unternehmen wie Last.fm,<br />
Facebook <strong>und</strong> der NewYork-Times eingesetzt [9].<br />
Das Projekt besteht aus mehreren Bestandteilen <strong>und</strong> Unterprojekten, unter anderem:<br />
Pig, Hive <strong>und</strong> HBase. Letzteres ist eine NoSQL Datenbank, die Ähnlichkeiten<br />
zu Cassandra aufweist, jedoch auf Hadoop basiert. Pig vereinfacht die Analyse<br />
<strong>von</strong> Daten <strong>und</strong> stellt häufig genutze Methoden, z.B. Joins bereit. Hive ist eine<br />
Datawarehouse-Applikation, die eine SQL-ähnliche Sprache zur Analyse der auf dem<br />
HDFS gespeicherten Daten implementiert. Im Folgenden wird ausschließlich auf die<br />
Bestandteile <strong>MapReduce</strong> <strong>und</strong> HDFS eingegangen, da sie in der Implementierung<br />
verwendet wurden.<br />
Eine wichtige Kernidee <strong>von</strong> <strong>MapReduce</strong> ist die Ausnutzung <strong>von</strong> Lokalität. Die Daten<br />
werden <strong>mit</strong>tels HDFS auf den verschiedenen Rechnern (Knoten) im Netzwerk<br />
gespeichert. Möchte eine Anwendung auf die Daten zugreifen, greift jeder Knoten<br />
4 http://nutch.apache.org/<br />
10
2.3 Apache Cassandra<br />
auf die Daten zu, die ihm lokal zur Verfügung stehen. Dadurch werden weniger Daten<br />
über das Netzwerk ausgetauscht <strong>und</strong> im Idealfall haben alle Knoten die gleiche<br />
Größe an Daten zu verarbeiten.<br />
Da<strong>mit</strong> dies möglich ist, müssen die Anwendungen dem Map-Reduce-Paradigma folgen.<br />
Das Paradigma besagt, dass jedes Programm, ein sogenannter Job, aus einer<br />
Map- <strong>und</strong> einer Reduce-Phase besteht. Ein Knoten verarbeitet zunächst mehrere<br />
Map-Tasks, die eine Teilmenge der Daten (Inputsplits) als Eingabe erhalten. Die<br />
Inputsplits sind lokal auf dem Knoten gespeichert. Eine Eingabe besteht immer aus<br />
einem Schlüssel <strong>und</strong> einem Wert, ebenso die Ausgabe der Mapper (MapInputKey,<br />
MapInputValue, MapOutputKey <strong>und</strong> MapOutputValue). Nach der Map-Phase werden<br />
die Daten anhand des MapOutputKey sortiert <strong>und</strong> zusammengefasst. Sie werden<br />
dann an Reduce-Tasks, die auf anderen Knoten laufen können, geschickt. Ein<br />
Reduce-Task erhält einen Schlüssel (ReduceInputKey) <strong>und</strong> eine Menge <strong>von</strong> Werten<br />
(ReduceInputValues) als Eingabe. Die Ausgabe der Reducer ist ebenfalls ein<br />
Schlüssel <strong>und</strong> ein zugehöriger Wert. Das Format des ReduceInputKey ist identisch<br />
<strong>mit</strong> dem Format des MapOutputKey. Die Eingabewerte sind die Werte, die sortiert<br />
<strong>und</strong> zusammengefasst worden sind, <strong>und</strong> den gleichen MapOutputKey haben. Es ist<br />
also gewährleistet, dass allen Reduce-Tasks jeweils alle diejenigen Werte zur Verfügung<br />
stehen, die <strong>mit</strong> dem gleichen MapOutputKey versehen worden sind. Schließlich<br />
werden nach der Verarbeitung in der Reduce-Phase ReduceOutputKey <strong>und</strong> ReduceOutputValue<br />
auf das HDFS geschrieben. Abb. 2.2 visualisiert den Workflow einer<br />
<strong>MapReduce</strong>-Anwendung[9, 10].<br />
Setzt man die Anzahl der Reduce-Tasks auf null, so erfolgt weder eine Sortierung<br />
noch eine weitere Verarbeitung durch den Reducer. Die Ergebnisse der Map-Phase<br />
werden direkt in das HDFS geschrieben. Das bedeutet, dass auch kein Austausch<br />
der Daten über das Netzwerk erfolgt, wie es beim Sortieren <strong>und</strong> Zusammenfassen<br />
vor der Reduce-Phase der Fall wäre.<br />
2.3 Apache Cassandra<br />
Klassiche SQL-Datenbanken haben sich in allen Bereichen der Informatik fest etabliert.<br />
Wachsende Datenmengen erfordern jedoch eine Möglichkeit der Skalierung bei<br />
der Analyse der Daten. Um dieser Nachfrage gerecht zu werden, entstanden die sogenannten<br />
NoSQL-Datenbanken („not only SQL“ oder „no SQL“). Eine Gemeinsam-<br />
11
2.3 Apache Cassandra<br />
Abbildung 2.2: <strong>MapReduce</strong> Workflow. Quelle: [10]<br />
keit aller NoSQL-Datenbanken, ist der Verzicht auf fixe Schemata zu Gunsten einer<br />
besseren horizontalen Skalierung. Weitere Gemeinsamkeiten sind die verteilte Speicherung<br />
der Daten, dynamische Anpassung der gespeicherten Daten <strong>und</strong> der Verzicht<br />
auf ACID-Eigenschaften wie sie bei SQL-Systemen üblich sind. Momentan gibt<br />
es mehr als h<strong>und</strong>ert verschiedene NoSQL-Datenbanken 5 <strong>mit</strong> verschiedenen Datenmodellen.<br />
Sie lassen sich u. a. in dokumentenorientierte Datenbanken (CouchDB 6 ),<br />
<strong>Graphen</strong>datenbanken (Neo4J 7 ) oder Objektdatenbanken (ZODB 8 ) einteilen. Cassandra<br />
fällt in die Kategorie Key-Value-Store [11, 12].<br />
Cassandras Datenmodell basiert auf der NoSQL-Datenbank Google Bigtable, während<br />
die Verteilung der Daten Gemeinsamkeiten <strong>mit</strong> Amazon Dynamo aufweist.<br />
Ursprünglich wurde es <strong>von</strong> Facebook entwickelt, um das Durchsuchen des Nachrichtensystems<br />
zu realisieren. Seit 2010 ist das Projekt ein Top-Level-Projekt der<br />
Apache Fo<strong>und</strong>ation.<br />
5 http://nosql-database.org/<br />
6 http://couchdb.apache.org/<br />
7 http://neo4j.org/<br />
8 http://www.zodb.org/<br />
12
2.3 Apache Cassandra<br />
2.3.1 Datenmodell<br />
Das Gr<strong>und</strong>element des Datenmodells ist die Spalte (engl. column). Eine Spalte besteht<br />
aus drei Elementen: einem Schlüssel, einem Wert <strong>und</strong> einem Zeitstempel. Der<br />
Zeitstempel wird verwendet, um eventuell auftretende Konflikte in der verteilten Datenmenge<br />
zu lösen. Der Schlüssel <strong>und</strong> der Wert lassen sich als Abbildung verstehen,<br />
in der der Schlüssel auf den Wert abgebildet wird. Eine Spalte kann auch weitere<br />
Spalten enthalten <strong>und</strong> wird in diesem Fall als Superspalte (engl. super column)<br />
bezeichnet. Superspalten haben keinen Zeitstempel, da die Subspalten Zeitstempel<br />
enthalten. Ein Datensatz besteht aus mehreren Spalten oder Superspalten <strong>und</strong> wird<br />
als Zeile (engl. row) bezeichnet. Der Datensatz wird eindeutig anhand seines Zeilenschlüssels<br />
(engl. row key) identifiziert [11].<br />
Datensätze werden in Spaltenfamilien (engl. column families) zusammengefasst, ähnlich<br />
den Tabellen in SQL. Eine Spaltenfamilie kann nur entweder Spalten oder Superspalten<br />
enthalten. Enthält sie Letzteres handelt es sich um eine Superspaltenfamilie.<br />
Im Gegensatz zum relationalen Datenmodell, hat nicht jeder Datensatz die gleiche<br />
Anzahl Columns. Ebenso können Datensätze frei um weitere Columns erweitert werden.<br />
In relationalen Datenbanken wäre eine Änderung der Tabelle notwendig. Mehre<br />
Spaltenfamilien werden in einem Keyspace zusammengefasst.<br />
Ein alternativer Ansatz ist das Betrachten des Datenmodells als multidimensionale<br />
Map. Abhängig da<strong>von</strong>, ob Superspaltenfamilien oder Spaltenfamilien verwendet<br />
werden, ergibt sich eine Map <strong>mit</strong> 4 oder 5 Dimensionen, die in Listing 2.5 dargestellt<br />
wird.<br />
Keyspace [ ColumnFamilyName ] [ Rowkey ] [ Columnname ] = value<br />
Keyspace [ ColumnFamilyName ] [ Rowkey ] [ SuperColName ] [ SubColName ] = value<br />
Listing 2.5: Cassandras Datenmodell als multidimensionale Map<br />
Abb. 2.3 zeigt den Aufbau einer Superspaltenfamilie anhand eines Beispiels. Die<br />
Studenten-Superspalten enthalten unterschiedliche Subspalten, da nicht zu jedem<br />
Studenten eine Handynummer existiert. Außerdem besuchen nicht alle Studenten die<br />
gleichen Veranstaltungen, sodass auch hier eine unterschiedliche Anzahl an Subspalten<br />
entsteht. Auch die Anzahl Superspalten ist nicht fix <strong>und</strong> könnte unterschiedlich<br />
sein.<br />
Die Daten werden beim Einfügen in die Datenbank sortiert, sodass keine nachträgliche<br />
Sortierung erfolgt. Eine Funktion wie „ORDER BY“ aus SQL gibt es nicht.<br />
13
2.3 Apache Cassandra<br />
Student<br />
(SuperColumnFamily)<br />
Rowkey<br />
:<br />
217654<br />
SuperColumn: Kontakt<br />
Telefon 004848<br />
Mobil 789705408<br />
SuperColumn: Noten<br />
Info 1 3,7<br />
Info 2 2,0<br />
Praktikum 1,3<br />
Rowkey<br />
:<br />
235678<br />
SuperColumn: Kontakt<br />
Telefon 4589<br />
ICQ 549879<br />
SuperColumn: Noten<br />
Info 1 1,7<br />
BWL 1 1,3<br />
eMail<br />
ex@example.org<br />
...<br />
Abbildung 2.3: Beispielhafte SuperColumnFamily<br />
Dadurch werden hohe Leseraten erzielt, jedoch kann die Sortierung nicht beim Lesezugriff<br />
festgelegt werden. Bei Spaltenfamilien erfolgt die Sortierung über den Namen<br />
der Spalte, bei Superspaltenfamilien über den Namen der Superspalten <strong>und</strong><br />
Subspalten. Der Datenbankentwickler kann zwischen verschiedenen vordefinierten<br />
Vergleichern (engl. Comparator) auswählen oder einen eigenen implementieren. In<br />
der Version 0.8.2 stellt Cassandra folgende Vergleicher zur Verfügung: AsciiType,<br />
BytesType, CompositeType, LexicalUUIDType, LongType, TimeUUIDType <strong>und</strong><br />
UTF8Type [11]. Beim Erstellen einer Spaltenfamilie wird jeweils ein Vergleicher für<br />
die Super- <strong>und</strong> Subspalten festgelegt.<br />
Während klassische relationale Datenbanken aus Tabellen bestehen, die über einen<br />
Verb<strong>und</strong> bei einer Anfrage <strong>mit</strong>einander verknüpft werden können, müssen bei Cassandra<br />
die Daten vorab denormalisiert werden. Die Daten werden bei der Denormalisierung<br />
mehrmals unterschiedlich formatiert in die Datenbank geschrieben, z.B.<br />
würden alle benötigten Verbünde vorab berechnet <strong>und</strong> abgespeichert werden.<br />
Eben Hewitt beschreibt in [11], dass es wichtig ist, sich bei der Entwicklung einer<br />
Anwendung mögliche Anfragen an die Datenbank vorab zu überlegen <strong>und</strong> die Daten-<br />
14
2.3 Apache Cassandra<br />
bank entsprechend aufzubauen. Hewitt empfiehlt eine Spaltenfamilie pro möglicher<br />
Art der Anfrage.<br />
2.3.2 Verteilung der Daten im Cluster<br />
Cassandra bietet verschiedene Möglichkeiten der Partitionierung. Es werden vordefinierte<br />
Partitionierer (engl. Partitioners) <strong>mit</strong>geliefert, die um eigene Partitionierer,<br />
die das IPartitioner Interface implementieren, erweitert werden können [11].<br />
Gr<strong>und</strong>sätzlich werden zur Bestimmung des Knotens, auf dem die Daten gespeichert<br />
werden sollen, der Zeilenschlüssel, der Replikationsfaktor (engl. replication factor)<br />
<strong>und</strong> die Replikationsstrategie verwendet 9 . Der Replikationsfaktor gibt an, wieviele<br />
Kopien der Daten im Cluster existieren. Ein Replikationsfaktor <strong>von</strong> 3 bedeutet<br />
beispielsweise, dass ein Datensatz dreimal im Cluster abgespeichert wird. Der Randompartitioner<br />
erlaubt es, die Daten gleichmäßig über den Cluster zu verteilen, da<br />
als Schlüssel der MD5-Hashwert des Zeilenschlüssels verwendet wird. Allerdings bedeutet<br />
dies auch, dass die Daten unsortiert vom System ausgegeben werden <strong>und</strong><br />
Anfragen, die gezielte Zeilenbereiche auswählen, ineffizient ausgeführt werden. Der<br />
OrderPreservingPartitioner verwendet die UTF8-Repräsentation des Zeilenschlüssels<br />
als Schlüssel. Folglich werden die Daten anhand des Zeilenschlüssels physisch<br />
abgespeichert. Je nach Zeilenschlüssel kann dies bedeuten, dass die Daten unregelmäßig<br />
über den Cluster verteilt werden, da die zu speichernden Daten oftmals nicht<br />
gleichverteilt sind. Wird als Zeilenschlüssel zum Beispiel der Nachname einer Person<br />
verwendet, so gäbe es eine Häufung bei den Knoten, die die Nachnamen, die<br />
<strong>mit</strong> „S“ beginnen, abspeichern. Die Last wäre also nicht regelmäßig verteilt. Vorteilhaft<br />
ist allerdings das Ausgeben sortierter Daten <strong>und</strong> die Möglichkeit, effizient<br />
Zeilenbereichsanfragen auszuwerten [11, 13].<br />
2.3.3 Datenzugriff<br />
Die Speicherung der Daten weist erhebliche Unterschiede zu relationalen Datenbanksystemen<br />
auf, folglich erfolgt der Datenzugriff ebenfalls auf eine andere Art.<br />
Cassandra ermöglicht den Zugriff der gespeicherten Daten per Thrift 10 <strong>und</strong> stellt<br />
hierfür eine API zur Verfügung. Die Entwickler <strong>von</strong> Cassandra empfehlen jedoch,<br />
9 http://wiki.apache.org/cassandra/FAQ#replicaplacement<br />
10 http://thrift.apache.org/<br />
15
2.3 Apache Cassandra<br />
nicht direkt per Thrift auf Cassandra zuzugreifen, sondern auf einen Client in der<br />
gewünschten Programmiersprache zurückzugreifen[11, 13].<br />
Der Konsistenzgrad (engl. consistency level) wird pro Schreib- oder Leseanfrage angegeben<br />
<strong>und</strong> definiert die Dauerhaftigkeit (engl. durability) der Anfrage. Die möglichen<br />
Konsistenzgrade bei einer Leseanfrage werden nachfolgend aufgelistet[11, 13]:<br />
ONE: Die Anfrage wird sofort vom ersten Knoten, an den die Anfrage gestellt wird,<br />
beantwortet. Dabei ist es möglich, dass das Ergebnis veraltet ist. Im Hintergr<strong>und</strong><br />
wird ein Prozess gestartet, der überprüft, ob die Daten auf den Knoten<br />
identisch sind, die gemäß Replikationsfaktor Kopien der Daten speichern. Ist<br />
dies nicht der Fall, wird das Datum verwendet, dessen Zeitstempel aktueller<br />
ist. Dieser Vorgang wird als „read repair“ bezeichnet.<br />
QUORUM: Die Anfrage wird erst beantwortet, wenn n + 1 Knoten die Daten auf<br />
2<br />
Konsistenz überprüft haben, wobei n der Replikationsfaktor ist. Im Hintergr<strong>und</strong><br />
wird ein „read repair“-Prozess gestartet, der alle Kopien im Cluster<br />
überprüft.<br />
ALL : Alle n Knoten, auf denen sich eine Kopie der Daten befindet, müssen die<br />
Konsistenz bestätigen. Die Anfrage schlägt fehl, wenn einer der Knoten nicht<br />
verfügbar ist.<br />
Werden die Konsistenzgrade „ONE“ oder „QUORUM“ verwendet, ist es möglich,<br />
dass die Anfrage einen veralteten Wert zurückliefert. Das Reparieren der Daten<br />
erfolgt in diesem Falle erst, nachdem der Wert zurückgegeben worden ist. Da alle<br />
weiteren Anfragen nach der Reparatur konsistent sind, wird die Konsistenz <strong>von</strong><br />
Cassandra als „eventually consistent“ bezeichnet.<br />
Beim Schreiben stehen weitere Konsistenzgrade zur Verfügung:<br />
ANY : Der Schreibvorgang muss nur <strong>von</strong> einem Knoten bestätigt werden, unabhängig<br />
vom Replikationsfaktor. Dabei wird ein sogenannter Hint als Bestätigung<br />
akzeptiert. Ein Hint wird dann zurückgeliefert, wenn ein Knoten, auf dem<br />
die Daten als Kopie vorliegen, nicht verfügbar ist. Cassandra speichert den<br />
Schreibvorgang zwischen <strong>und</strong> schreibt die Daten, sobald der Knoten wieder<br />
erreichbar ist.<br />
ONE : Dieser Konsistenzgrad verhält sich wie „ANY“, jedoch reicht ein Hint als<br />
Bestätigung nicht aus.<br />
16
2.3 Apache Cassandra<br />
QUORUM : n + 1 Knoten, wobei n der Replikationsfaktor ist, müssen den Schreibvorgang<br />
bestätigen.<br />
2<br />
ALL : Alle n Knoten müssen den Schreibvorgang bestätigen.<br />
Nachdem ein Konsistenzgrad ausgewählt worden ist, kann nun der Zugriff auf die<br />
Daten erfolgen. Wird das Datenmodell als multidimensionale Map betrachtet, erfolgt<br />
der Zugriff durch Angeben der Schlüssel. Je nachdem, welche Schlüssel angegeben<br />
worden sind, werden Subspalten, Superspalten oder ganze Zeilenbereiche zurückgegeben.<br />
Eine Filterung der gewünschten Daten ist durch Definition <strong>von</strong> Wertebereichen<br />
(engl. ranges) möglich. Wird bei einer Anfrage ein SlicePredicate definiert,<br />
so werden nur die dadurch definierten Spalten zurückgegeben. Dabei gibt es zwei<br />
Möglichkeiten, die gewünschten Spalten zu definieren. Entweder werden die Spaltennamen<br />
explizit angegeben oder es wird ein Bereich definiert, der durch einen<br />
Anfangs- <strong>und</strong> Endschlüssel gegeben ist. Die Daten zwischen Start <strong>und</strong> Ende ergeben<br />
sich aus dem verwendeten Vergleicher. Per SlicePredicate kann außerdem eine<br />
Li<strong>mit</strong>ierung der Spaltenanzahl („count“) <strong>und</strong> die Umkehrung der Reihenfolge („reversed“)<br />
erfolgen. Es ist nicht möglich ein SlicePredicate für Subspalten zu definieren.<br />
Wird eine Superspalte ausgewählt, werden immer alle Subspalten serialisiert. Eine<br />
Anwendung, die nicht alle Subspalten benötigt, muss also nach der Serialisierung<br />
filtern.<br />
Die Funktionen get_range_slices <strong>und</strong> multi_get_range der Cassandra API<br />
erlauben es, die resultierenden Zeilen der Anfrage einzuschränken. Sie erfordern aber,<br />
um effektiv genutzt werden zu können, den Einsatz des veralteten OrderPreserving-<br />
Partitioner. Eine Übersicht aller weiteren Funktionen findet sich in [11, 13].<br />
17
3 Strategien zur <strong>Auswertung</strong> <strong>und</strong><br />
Speicherung der <strong>RDF</strong>-Daten<br />
In diesem Kapitel werden die Strategien zur Speicherung <strong>und</strong> <strong>Auswertung</strong> der <strong>RDF</strong>-<br />
Daten <strong>mit</strong> Cassandra <strong>und</strong> Hadoop beschrieben. Details der Implementierung werden<br />
in Kapitel 4 erläutert.<br />
3.1 Speicherstrategie<br />
Die Speicherstrategie der Implementierung basiert auf einem Vorschlag, der <strong>von</strong><br />
Ladwig <strong>und</strong> Harth in [14] vorgestellt wurde. Die Autoren präsentieren ein Speicherschema<br />
für <strong>RDF</strong>-Daten, sowie einen <strong>RDF</strong>-Layer <strong>und</strong> <strong>RDF</strong>-Store (Cumulus<strong>RDF</strong>) für<br />
Cassandra. Die Daten werden insgesamt dreimal abgespeichert, um schnelles Nachschlagen<br />
aller acht möglichen Tripelmuster zu gewährleisten. Zum Speichern der Daten<br />
werden in Cassandra drei Superspaltenfamilien verwendet: „SPO“, „POS“ <strong>und</strong><br />
„OSP“. Die Tripel werden auf Zeilenschlüssel, Superspaltenname <strong>und</strong> Subspaltenname<br />
abgebildet, wobei der Wert der Subspalte leer gelassen wird. Dies ist notwendig,<br />
um mehrwertige Prädikate zu unterstützen. In der Spaltenfamilie „SPO“ wird das<br />
Subjekt als Zeilenschlüssel, das Prädikat als Superspaltenname <strong>und</strong> das Objekt als<br />
Subspaltenname verwendet. Äquivalent werden die Tripel für die Spaltenfamilien<br />
„POS“ <strong>und</strong> „OSP“ abgebildet. Abb. 3.1 verdeutlicht die Speicherstrategie anhand<br />
eines Beispiels. Die <strong>RDF</strong>-Daten modellieren Bekanntschaften zwischen Benutzern,<br />
sowie deren Alter. Es werden die drei Superspaltenfamilien, sowie die zugehörigen<br />
Schlüssel <strong>und</strong> Spaltennamen aufgeführt. Aus Platzgründen werden lediglich Auszüge<br />
gezeigt.<br />
18
3.1 Speicherstrategie<br />
data.n3<br />
...<br />
ex:Tom ex:userKnows ex:Jerry<br />
ex:Tom ex:userKnows ex:Chris<br />
ex:Jerry ex:userKnows ex:Chris<br />
…<br />
ex:Tom ex:userAge 28<br />
ex:Tom ex:userAge 25<br />
...<br />
Cluster: Cluster01<br />
Keyspace: <strong>RDF</strong>-Data<br />
SPO<br />
ex:Jerry<br />
ex:userKnows<br />
ex:userAge<br />
ex:Tom „“<br />
ex:Chris „“<br />
...<br />
25 „“<br />
...<br />
...<br />
ex:Tom<br />
ex:userKnows<br />
ex:Jerry „“<br />
ex:Chris „“<br />
...<br />
...<br />
ex:userAge<br />
28 „“<br />
...<br />
...<br />
POS<br />
ex:userKnows<br />
ex:Chris<br />
ex:Tom<br />
ex:Tom „“<br />
ex:Jerry „“<br />
...<br />
ex:Jerry „“<br />
...<br />
...<br />
ex:userAge<br />
ex:25<br />
ex:28<br />
ex:Jerry „“ ex:Tom „“<br />
...<br />
...<br />
...<br />
OSP<br />
...<br />
25<br />
ex:Jerry<br />
ex:userAge „“<br />
...<br />
ex:Chris<br />
...<br />
ex:Tom<br />
ex:userKnows „“<br />
...<br />
...<br />
...<br />
Abbildung 3.1: Beispielhafte Speicherung der Daten<br />
19
3.1 Speicherstrategie<br />
Tab. 3.1 führt die möglichen Tripelmuster auf, sowie die Spaltenfamilien, die zur<br />
<strong>Auswertung</strong> verwendet werden können. Es wird bei dieser Strategie versucht, möglichst<br />
viele Schlüssel beim Zugriff festzulegen. Die Schlüssel werden immer in der<br />
Reihenfolge Zeilenschlüssel, Superspaltenname <strong>und</strong> Subspaltenname festgelegt. Bei<br />
einer Variablen werden folglich Zeilenschlüssel <strong>und</strong> Superspaltenname (via SlicePredicate)<br />
beim Zugriff festgelegt. Bei zwei Variablen im Tripelmuster kann nur der<br />
Zeilenschlüssel festgelegt werden. Bei dieser Strategie muss also nur im Falle <strong>von</strong><br />
drei Variablen über die Zeilenschlüssel iteriert werden. Dadurch wird das Ergebnis<br />
der Anfrage frühzeitig eingeschränkt. Ein wesentlicher Nachteil ist der hohe Speicherplatzbedarf.<br />
Ein Datum wird in drei verschiedenen Spaltenfamilien gespeichert.<br />
Wird zusätzlich ein Replikationsfaktor größer 1 gewählt, existieren weitere Kopien<br />
der Daten. Bei einem Replikationsfaktor <strong>von</strong> 3 ergäben sich so<strong>mit</strong> 9 Kopien (insgesamt)<br />
der Daten im System.<br />
Triple Pattern Index<br />
(s, p, o) SPO, POS, OSP<br />
(s, p, ?) SPO<br />
(?, p, o) POS<br />
(s, ?, o) OSP<br />
(?, p, ?) POS<br />
(s, ?, ?) SPO<br />
(?, ?, o) OSP<br />
(?, ?, ?) SPO, POS, OSP<br />
Tabelle 3.1: Tripelmuster <strong>mit</strong> zu verwendender Spaltenfamilie. Quelle: [14]<br />
Der <strong>RDF</strong>-Store Cumulus<strong>RDF</strong>[14] unterstützt keine leeren Knoten, da hier ein Verb<strong>und</strong><br />
notwendig ist. Die Autoren beschränken sich aber hier auf das Nachschlagen<br />
<strong>von</strong> Tripelmustern.<br />
Implementierungen, die <strong>RDF</strong>-Daten auf Hadoops verteilten Dateisystem speichern,<br />
nutzen häufig das Prinzip der vertikalen Partitionierung aus. Die <strong>RDF</strong>-Tripel werden<br />
anhand des Prädikats in Dateien aufgeteilt. Bei der <strong>Auswertung</strong> <strong>von</strong> Anfragen<br />
muss, sofern ein Prädikat gegeben ist, nur die Datei geladen werden, die das Prädikat<br />
enthält. Es wurde versucht diesen Ansatz auf Cassandra zu übertragen, indem man<br />
pro Prädikat einen Keyspace anlegt. Allerdings zeigte sich, dass Cassandra Datenmodell<br />
nicht zur Verwendung vieler Keyspaces ausgelegt ist. So wird der Keyspace<br />
gewöhnlich bei der Initialisierung festgelegt, danach folgen die Anfragen auf diesen<br />
Keyspace. Außerdem müsste zur Beantwortung <strong>von</strong> Anfragen <strong>mit</strong> einer Variable als<br />
20
3.1 Speicherstrategie<br />
Prädikat ein Index gespeichert werden, der alle Keyspace-Namen enthält. Mit dessen<br />
Hilfe würde dann über alle Keyspaces iteriert.<br />
Bei der Implementierung wurde festgestellt, dass die Verteilung der Daten auf die<br />
Knoten im Cluster <strong>mit</strong> Hadoop nicht optimal ist. Die Rechenlast wird nicht ausgeglichen<br />
verteilt, sondern wird nur einem Knoten zugewiesen. Dies liegt an den<br />
ausgewählten Spaltenfamilien, sowie dem frühzeitigen Festlegen des Zeilenschlüssels.<br />
Daher werden für den ersten Verb<strong>und</strong>, bei dem die Daten <strong>von</strong> Hadoop geladen<br />
werden, andere Spaltenfamilien verwendet. Details zur Optimierung werden in<br />
Kap. 4.2.4 erläutert.<br />
21
3.2 <strong>Auswertung</strong>sstrategie<br />
3.2 <strong>Auswertung</strong>sstrategie<br />
Die vorgestellte Implementierung unterstützt Anfragen, die aus Basic Graph Patterns<br />
<strong>mit</strong> bis zu 2 Variablen pro Tripelmuster bestehen. Zur <strong>Auswertung</strong> wird für<br />
jedes konsekutive Paar an Tripelmustern ein Verb<strong>und</strong> (engl. join) über die gemeinsamen<br />
Variablen dieser Tripel durchgeführt. Pro Verb<strong>und</strong> wird ein <strong>MapReduce</strong>-Job<br />
generiert, der die Daten aus Cassandra lädt <strong>und</strong> das Zwischen- oder Endergebnis<br />
auf das HDFS schreibt. Für n Tripel sind also n-1 <strong>MapReduce</strong>-Jobs nötig, um die<br />
Anfrage zu beantworten.<br />
Abb. 3.2 zeigt den Datenfluss der <strong>MapReduce</strong>-Jobs. Der erste der insgesamt n-1 Join-<br />
Jobs lädt den ersten <strong>und</strong> zweiten Datensatz aus Cassandra. Die Zwischenergebnisse<br />
werden auf das HDFS in einem temporären Ordner gespeichert. Der zweite Job<br />
erhält die Zwischenergebnisse vom HDFS, sowie den zweiten Datensatz aus Cassandra.<br />
Der Job speichert die Zwischenergebnisse auf das HDFS. Dieser Vorgang<br />
wiederholt sich bis zum letzten Job, der das Endergebnis auf das HDFS schreibt.<br />
Das Endergbnis enthält alle resultierenden Abbildungen der Variablen auf einen der<br />
Anfrage entsprechenden Wert.<br />
Cassandra<br />
1.Input laden<br />
2.Input laden<br />
2.Input<br />
Join 1 Join 2 Join n-1<br />
...<br />
Zwischenergebnisse<br />
laden<br />
Zwischenergebnisse<br />
speichern<br />
Zwischenergebnisse<br />
speichern<br />
Zwischenergebnisse<br />
laden<br />
Endergebnis<br />
speichern<br />
HDFS<br />
Abbildung 3.2: Job-Sequenz <strong>mit</strong> Datenfluss<br />
22
3.2 <strong>Auswertung</strong>sstrategie<br />
Der Verb<strong>und</strong> wird als sogenannter Map-Side-Join realisiert. Das bedeutet, dass der<br />
Verb<strong>und</strong> innerhalb der Map-Phase des <strong>MapReduce</strong>-Programms bereits abgeschlossen<br />
wird. Bei einem Verb<strong>und</strong> werden Tripel, die sich aus einem linken Tripelmuster<br />
ergeben, <strong>mit</strong> Tripeln, die <strong>von</strong> einem rechten Tripelmuster stammen, verb<strong>und</strong>en. In<br />
der Map-Phase werden die Tripel der linken Seite geladen <strong>und</strong> es werden dynamisch<br />
für jedes dieser Tripel die zu verbindenden Tripel der rechten Seite geladen. Da<br />
auf eine Reduce-Phase bei dieser Methode verzichtet wird, wird die Zeit eingespart,<br />
die sonst nötig wäre, um die Daten zu sortieren <strong>und</strong> nach Schlüssel zu gruppieren.<br />
Schließlich wird auch Netzwerkverkehr eingespart, da die Daten nicht an die Reducer<br />
geschickt werden müssen.<br />
In der Map-Phase wird jeweils über die Daten iteriert, die sich aus dem linken<br />
Tripelmuster ergeben. Es ergibt sich eine Menge an Abbildungen <strong>von</strong> der Menge<br />
der Variablen auf die jeweiligen Werte, die nachfolgend Mapping genannt wird. Für<br />
jedes Mapping werden nun die Daten, die verb<strong>und</strong>en werden sollen, angefragt <strong>und</strong><br />
geladen. Dabei wird ausgenutzt, dass es eine Schnittmenge der Variablen zwischen<br />
den beiden Tripelmustern gibt.<br />
Linkes Tripelmuster:<br />
?inproc rdf:Type bench: Inproceeding<br />
Rechtes TripelMuster:<br />
?inproc dc:creator ?author<br />
Mappings:<br />
Gemeinsame Variable: ?inproc<br />
{?author =persons:Eileen_Heick,<br />
?inproc=inproc:Inproceeding1}<br />
{?author=persons:Paul_Erdoes,<br />
?inproc=inproc:Inproceeding1}<br />
Iteration<br />
{?inproc=inproc:Inproceeding1}<br />
Iteration<br />
{?inproc=inproc:Inproceeding2}<br />
{?inproc=inproc:Inproceeding3}<br />
null<br />
null<br />
{?inproc=inproc:Inproceeding4}<br />
{?author=persons:Gizla_Nordmark,<br />
?inproc=inproc:Inproceeding4}<br />
Iteration<br />
{?author=persons:Eileen_Heick, ?inproc=inproc:Inproceeding1}<br />
{?author=persons:Paul_Erdoes, ?inproc=http:inproc:Inproceeding1}<br />
{?author=persons:Gizla_Nordmark, ?inproc=inproc:Inproceeding4}<br />
Abbildung 3.3: Beispielhafter Verb<strong>und</strong> in der Map-Phase des Jobs<br />
Abb. 3.3 verdeutlicht den Verb<strong>und</strong> anhand eines Beispiels. Die linke <strong>und</strong> rechte Seite<br />
23
3.2 <strong>Auswertung</strong>sstrategie<br />
bilden jeweils Mappings (Abbildungen) der Variablen auf den jeweiligen Wert, der<br />
in der Zeile in Cassandra vorliegt oder vom HDFS geladen wurde, wenn es sich um<br />
ein Zwischenergebnis handelt. Im Beispiel wird über alle möglichen Werte für die<br />
Variable ?inproc iteriert. Durch die vorhandene Schnittmenge der linken <strong>und</strong> rechten<br />
Seite ergeben sich vor dem Zugriff auf die Datenbank Werte für die gemeinsamen<br />
Variablen. Nun wird für die rechte Seite dynamisch auf Cassandra zugegriffen. Dabei<br />
werden die bekannten Schlüssel, die sich aus den gemeinsamen Variablen ergeben,<br />
berücksichtigt. Im Beispiel wird also erkannt, dass ?inproc eine gemeinsame Variable<br />
ist. Folglich gibt es für die rechte Seite nur noch die Variable ?author, da der<br />
jeweilige Wert für ?inproc <strong>von</strong> der linken Seite übernommen wird. Es gibt nun zwei<br />
Möglichkeiten: Entweder erhält man eine Menge an Mappings, über die man nun<br />
iteriert <strong>und</strong> das Kreuzprodukt <strong>mit</strong> dem linken Mapping bildet oder es gibt kein<br />
Mapping für die rechte Seite. Dies ist dann der Fall, wenn die Cassandra-Datenbank<br />
für die definierten Schlüssel keinen Wert liefert. In diesem Fall wird diese Zeile nicht<br />
im Verb<strong>und</strong> berücksichtigt. Im Beispiel liegen also für inproc:Inproceeding2 <strong>und</strong> inproc:Inproceeding3<br />
keine Daten über die Autoren vor, sodass der Verb<strong>und</strong> für diese<br />
Werte nicht berechnet wird. Für die anderen Werte liegen Daten über die Autoren<br />
vor, sodass das Kreuzprodukt berechnet werden kann.<br />
24
4 Implementierung <strong>und</strong><br />
Optimierungen<br />
Insgesamt wurden zwei Anwendungen implementiert. Die erste Anwendung dient<br />
zum Speichern <strong>von</strong> <strong>RDF</strong>-Daten in Cassandra. Sie erhält eine im N3-Format vorliegende<br />
<strong>RDF</strong>-Datei als Eingabe <strong>und</strong> speichert die Daten in die drei Spaltenfamilien<br />
„SPO“, „POS“ <strong>und</strong> „OSP“ (siehe Kap. 3.1). Der Benutzer kann den Keyspace festlegen,<br />
sowie die Anzahl der Tripel, die pro Batch in die Datenbank geschrieben werden,<br />
um die Schreibgeschwindigkeit zu regulieren. Die Anwendung wird auf einem Knoten<br />
des Clusters geladen <strong>und</strong> dort ausgeführt.<br />
Die zweite Anwendung, die der <strong>Auswertung</strong> der SPARQL-BGP Anfragen dient, wird<br />
<strong>mit</strong> Hadoop gestartet <strong>und</strong> läuft verteilt auf dem Cluster. Als Eingabe erhält das<br />
Programm die Anfrage, sowie den Namen des Keyspace, auf dem die <strong>Auswertung</strong><br />
durchgeführt werden soll. Die Ausgabe erfolgt auf dem HDFS. Abb. 4.1 zeigt die<br />
Systemarchitektur. Links ist die Anwendung zur <strong>Auswertung</strong> der SPARQL-BGP-<br />
Anfragen zu sehen, während rechts das Einleseprogramm dargestellt wird.<br />
Beide Anwendungen wurden in der Programmiersprache Java implementiert. Der<br />
Hector Client 1 ermöglicht den Zugriff auf Cassandra in Java-Programmen <strong>und</strong> wird<br />
in beiden Anwendungen verwendet. Das selbsternannte Ziel <strong>von</strong> Hector ist es, dem<br />
Entwickler Funktionen zur Verfügung zu stellen, um einfacher auf Cassandra zuzugreifen.<br />
Dabei greift der Client selbst per Thrift-API auf Cassandra zu <strong>und</strong> ist so<strong>mit</strong><br />
ein Wrapper dieser Funktionen. Die <strong>MapReduce</strong>-Jobs greifen per ColumnFamilyInputFormat<br />
<strong>und</strong> Hector-Client auf die Datenbank zu. Das ColumnFamilyInputFormat<br />
wird vom Cassandra-Projekt zur Verfügung gestellt, um Daten aus Cassandra <strong>mit</strong><br />
<strong>MapReduce</strong> zu verarbeiten.<br />
1 http://prettyprint.me/2010/02/23/hector-a-java-cassandra-client/<br />
25
4.1 Einlesen der Daten<br />
Abbildung 4.1: Systemarchitektur der Implementierung<br />
4.1 Einlesen der Daten<br />
Die Daten liegen im N3-Format auf dem Server vor. Zum Einlesen wurde ein Programm<br />
implementiert, das die Daten <strong>mit</strong> dem Parser des Sesame-Frameworks 2 in<br />
Aussagen unterteilt. Die Präfixe werden vom Parser automatisch erweitert, sodass<br />
die URIs in voller Länge abgespeichert werden. Zum Schreiben der Daten in die<br />
Cassandra-Datenbank wird der Hector-Client eingesetzt. Die Laufzeit wird verbessert,<br />
wenn die Funktion addInsertion statt insert verwendet wird. Auf diese Weise<br />
werden die Daten zunächst als sogenannter Batch gesammelt <strong>und</strong> dann gleichzeitig<br />
in die Datenbank geladen. Batchgrößen zwischen 500 <strong>und</strong> 2000 lieferten gute Ergebnisse.<br />
Die Laufzeit verbessert sich bei größeren Batchgrößen wenig, aber das Risiko<br />
einer OutOfMemory-Exception steigt, da die Daten auf den Heap der Java Virtual<br />
Machine zwischengespeichert werden.<br />
2 http://www.openrdf.org/<br />
26
4.2 Auswerten der Anfragen<br />
4.2 Auswerten der Anfragen<br />
Abb. 4.2 zeigt den Datenfluss der ersten Anwendung. Nach dem Parsen werden aus<br />
den Tripelmustern die <strong>MapReduce</strong> Jobs erzeugt. Anschließend wird die Sequenz<br />
dieser Jobs ausgeführt.<br />
Anfrage<br />
ARQ Parser<br />
●<br />
Parsen der Anfrage<br />
●<br />
Aufteilung in Tripelmuster<br />
Tripelmuster<br />
JobCreator<br />
JoinConfigHelper<br />
●<br />
Erzeugung der <strong>MapReduce</strong>-Jobs<br />
●<br />
Festlegung <strong>von</strong> Spaltenfamilien <strong>und</strong><br />
Prädikaten in der Konfiguration<br />
<strong>MapReduce</strong>-Jobs <strong>mit</strong> Konfigurationen<br />
●<br />
Ausführung der Jobs auf dem<br />
Cluster<br />
●<br />
Zugriff auf Cassandra<br />
●<br />
Verb<strong>und</strong> erfolgt als Map-Side-Join<br />
●<br />
Ausgabe auf HDFS<br />
Ergebnis<br />
Abbildung 4.2: Übersicht des Programmverlaufs<br />
4.2.1 Generieren der Jobs<br />
Die Anfrage wird zuerst an den ARQ-Parser des Jena-Frameworks 3 übergeben <strong>und</strong><br />
wird in Tripelmuster aufgeteilt. Diese werden an die Klasse JobCreator übergeben.<br />
3 http://jena.sourceforge.net/ARQ/<br />
27
4.2 Auswerten der Anfragen<br />
Die Klasse JobCreator erstellt anhand dieser Eingabe eine Liste <strong>von</strong> <strong>MapReduce</strong>-<br />
Jobs <strong>mit</strong> jeweils eigener Konfiguration. Zum Erstellen der Konfiguration wird die<br />
Klasse JoinConfigHelper verwendet, die Funktionen zum Lesen <strong>und</strong> Schreiben der<br />
Programm-Parameter bereitstellt. Für jeden Job wird nun die Eingabe, Ausgabe,<br />
Mapper-Klasse, sowie weitere Parameter, die vom Hector Client benötigt werden,<br />
festgelegt. Die Ausgabe ist eine Liste ausführbarer <strong>MapReduce</strong>-Jobs, die einen im<br />
Folgenden beschriebenen Map-Side-Join durchführen.<br />
4.2.2 Map-Side Join<br />
Der Verb<strong>und</strong> (engl. join), der zur <strong>Auswertung</strong> mehrerer Tripelmuster notwendig ist,<br />
wurde wie in Kap. 3.2 beschrieben als Map-Side-Join implementiert. Die Daten werden<br />
beim ersten Verb<strong>und</strong> per ColumnFamilyInputFormat für Hadoop in die Mapper<br />
geladen. In jedem weiteren Verb<strong>und</strong> wird das FileInputFormat verwendet, um die<br />
Zwischenergebnisse vom HDFS zu laden. Werden die Daten vom HDFS geladen,<br />
liegen sie bereits als Mapping vor. Werden sie aus Cassandra geladen, muss daraus<br />
zunächst ein Mapping erzeugt werden. Zu jedem Verb<strong>und</strong> gibt es ein linkes <strong>und</strong><br />
ein rechtes Tripelmuster. Aus jedem Tripelmuster der Anfrage ergibt sich gemäß<br />
der vorgestellten Speicherstrategie eine Spaltenfamilie. Außerdem werden, je nach<br />
Anzahl der Variablen, Namen für Zeilenschlüssel, Super- <strong>und</strong> Subspalten anhand<br />
der Konfiguration festgelegt. Jeder Mapper lädt die Daten, die lokal auf seinem<br />
Cassandra-Knoten zur Verfügung stehen. Da jeweils zwei Mapper-Klassen, für das<br />
Laden der Daten aus Cassandra oder dem HDFS, benötigt werden, wurde die Durchführung<br />
des Verb<strong>und</strong>s ausgelagert. Die Berechnung des Verb<strong>und</strong>s erfolgt stattdessen<br />
<strong>mit</strong> der Klasse JoinPerformer, um red<strong>und</strong>anten Code zu vermeiden.<br />
4.2.3 Alternativen zum Map-Side Join<br />
Alternativ zum vorgestellten Map-Side Join gibt es Map-Side-Merge-Joins <strong>und</strong> Reduce-<br />
Side-Joins. Im Folgenden werden die Funktionsweisen kurz vorgestellt <strong>und</strong> begründet,<br />
warum eine Implementierung dieser Verbünde nicht möglich war.<br />
Ein Reduce-Side-Join markiert die Daten während der Map-Phase, sodass in der<br />
Reduce-Phase alle Daten so gruppiert sind, dass ein Reducer alle Datensätze hat, die<br />
verb<strong>und</strong>en werden müssen. Um die Daten zu laden, wird die MultipleInputs-Klasse<br />
verwendet, die es erlaubt verschiedene Datenquellen als Input festzulegen <strong>und</strong> ihnen<br />
28
4.2 Auswerten der Anfragen<br />
jeweils einen eigenen Mapper zuzuweisen. Dies funktioniert problemlos, wenn die Daten<br />
vom HDFS <strong>und</strong> Cassandra geladen werden sollen, was ab dem zweiten Join der<br />
Fall ist. Der erste Join, bei dem aus zwei Spaltenfamilien die Daten geladen werden,<br />
bereitet jedoch Probleme. Die Ursache befindet sich hauptsächlich in den Klassen<br />
ColumnFamilyInputformat <strong>und</strong> JoinConfigHelper. Diese Klassen sind so ausgelegt,<br />
dass nur eine Spaltenfamilie <strong>und</strong> SlicePredicate pro Job ausgewählt werden kann. Es<br />
wurde versucht die Klassen um diese Funktionalität zu erweitern. Dies gelang auch<br />
für kleinere Inputdateien. Ab einer bestimmten Größe der Input-Dateien schlugen<br />
die Jobs <strong>mit</strong> einer OutOfMemory-Ausnahme fehl. Eine Implementierung gestaltete<br />
sich so<strong>mit</strong> schwierig <strong>und</strong> konnte aus Zeitgründen nicht fertiggestellt werden, da<br />
tiefgreifendere Änderungen am bereitgestellten Quellcode nötig gewesen wären.<br />
Ein Map-Side-Merge-Join hat viele Gemeinsamkeiten <strong>mit</strong> dem implementierten Map-<br />
Side-Join. Bei einem solchen Verb<strong>und</strong> wird ebenfalls auf die Reduce-Phase verzichtet.<br />
Allerdings wird versucht durch gezieltes Partitionieren <strong>und</strong> Sortieren der Daten<br />
den Verb<strong>und</strong> schon beim Laden der Daten in den Mapper zu sortieren. Es gibt<br />
verschiedene Vorraussetzungen für einen Map-Side-Merge-Join [15]:<br />
1. Beide Datensätze müssen identisch partitioniert <strong>und</strong> sortiert sein. Ist dies der<br />
Fall muss der Verb<strong>und</strong> der ersten Partition des ersten Datensatzes <strong>mit</strong> der<br />
ersten Partition des zweiten Datensatzes berechnet werden. Anschließend wird<br />
der Verb<strong>und</strong> der weiteren Partitionen berechnet bis schließlich der gesamte<br />
Verb<strong>und</strong> berechnet worden ist.<br />
2. Der Typ der Input- <strong>und</strong> Output-Schlüssel muss identisch sein, da sonst die<br />
Daten nicht identisch partitioniert würden.<br />
Das Hadoop-Framework stellt zu diesem Zweck die Klasse CompositeInputformat<br />
bereit. Zunächst trat das gleiche Problem wie beim Reduce-Side-Join auf, da die<br />
Daten aus mehreren Spaltenfamilien geladen werden müssen. Ein weiteres Problem<br />
betraf die Ordnung <strong>und</strong> Partitionierung. Datensätze werden in Cassandra prinzipiell<br />
so geordnet, wie sie geschrieben werden. In der Regel erfolgt also keine Ordnung der<br />
Schlüssel. Verwendet man den OrderPreservingPartitioner, ist zwar eine Ordnung<br />
gegeben, die Daten werden aber dann nicht mehr gleichmäßig im Cluster verteilt.<br />
Außerdem werden nicht alle Datensätze über den Zeilenschlüssel verb<strong>und</strong>en. Insgesamt<br />
wären sowohl tiefgreifendere Änderungen am Quelltext vorzunehmen, als auch<br />
das Speicherverfahren soweit abzuändern, da<strong>mit</strong> die Daten richtig partitioniert würden.<br />
29
4.2 Auswerten der Anfragen<br />
4.2.4 Optimierung für Hadoop<br />
Für den ersten Verb<strong>und</strong> wird das ColumnFamilyInputFormat für Hadoop, das <strong>von</strong><br />
Cassandra zur Verfügung gestellt wird, verwendet. Ist der Cluster richtig konfiguriert,<br />
greift der Mapper auf die Daten zu, die lokal auf seinem Knoten in Cassandra<br />
gespeichert werden. Folglich wird Lokalität ausgenutzt <strong>und</strong> die Daten können verteilt<br />
ausgewertet werden. Es wird am folgenden Beispiel (Listing 4.1) erläutert, warum<br />
das verwendete Nachschlageverfahren für den ersten Verb<strong>und</strong> nicht sinnvoll ist:<br />
ex : Thomas ex : userKnows ? person<br />
Listing 4.1: Tripelmuster einer SPARQL-BGP-Anfrage<br />
Nach dem oben beschriebenen Nachschlageverfahren sollte die ColumnFamily „SPO“<br />
verwendet werden. Zeilenschlüssel <strong>und</strong> Spaltenname sind bekannt <strong>und</strong> es muss nur<br />
über alle Subspalten itereriert werden, die <strong>mit</strong> Daten, die aus einem weiteren Tripel<br />
gewonnen werden, verb<strong>und</strong>en werden. Es ergibt sich jedoch ein entscheidender<br />
Nachteil. Die Daten werden auf die Knoten per Random-Partitioner verteilt, indem<br />
der Zeilenschlüssel verwendet wird. Eine Zeile wird komplett auf einem Knoten<br />
abgespeichert. Je nach Replikationsfaktor gibt es weitere Kopien, die aber bei der<br />
Verwendung des ColumnFamilyInputformat nicht ausreichend verwendet werden.<br />
Da der Schlüssel im Beispiel <strong>mit</strong> “ex:Thomas” festgelegt wurde, werden die Daten<br />
nur auf einem Knoten lokal verfügbar sein <strong>und</strong> auch nur hier verarbeitet. Dies hat<br />
zur Folge, dass nur ein Knoten den Verb<strong>und</strong> ausführt, während die anderen Knoten<br />
schnell ihre Map-Tasks abarbeiten <strong>und</strong> untätig für die weitere Job-Ausführung<br />
bleiben.<br />
Abbildung 4.3: Zustand der Mapper ohne Optimierung (Bildschirmaufnahme)<br />
30
4.2 Auswerten der Anfragen<br />
Abb. 4.3 veranschaulicht diesen Sachverhalt in einem Cluster <strong>mit</strong> 9 Knoten. 8 der 9<br />
Knoten sind nach wenigen Sek<strong>und</strong>en fertig, während der letzte Knoten sehr lange<br />
rechnet. Der Kreis in der Grafik verdeutlicht, dass der 9. Map-Task noch nicht<br />
fertiggestellt worden ist. Bei der <strong>Auswertung</strong> der Anfrage Q3A auf 25 Millionen<br />
Tripel dauerte der Verb<strong>und</strong> 39 Minuten, da eine Teilung des Rechenaufwands nicht<br />
stattfand.<br />
Beim Zugriff auf Cassandra wird die Spaltenfamilie so gewählt, dass der Zeilenschlüssel<br />
festgelegt werden kann. Ändert man das Verfahren so ab, dass der Zeilenschlüssel<br />
nicht festgelegt wird <strong>und</strong> so<strong>mit</strong> eine andere Spaltenfamilie festgelegt wird, werden alle<br />
Knoten am Verb<strong>und</strong> beteiligt. Bei dem obigen Beispiel wird auf die Spaltenfamilie<br />
„OSP“ zugegriffen. Es wird ein SlicePredicate <strong>mit</strong> dem Namen „ex:Thomas“ festgelegt<br />
<strong>und</strong> außerdem ein Subspaltenname „ex:userknows“. Es wird auf allen Knoten<br />
über alle Objekte iteriert.<br />
Abbildung 4.4: Zustand der Mapper nach der Optimierung (Bildschirmaufnahme)<br />
Abb. 4.4 zeigt den Zustand der Mapper wenige Sek<strong>und</strong>en nach Programmstart <strong>und</strong><br />
illustriert die Optimierung. Alle Knoten führen Map-Tasks aus, deren Anzahl kann<br />
außerdem vom ColumnFamilyInputFormat erhöht werden (<strong>von</strong> 9 auf 117). Die Ausführung<br />
verbesserte sich durch diese Optimierung bei 25 Millionen Tripeln <strong>von</strong> 39 auf<br />
2 Minuten. Für die acht möglichen Tripelmuster ergeben sich die Spaltenfamilien,<br />
die in Tab. 4.1 aufgeführt sind.<br />
Enthält ein Tripelmuster zwei Variablen, werden diese auf den Zeilenschlüssel <strong>und</strong><br />
Subspaltenname abgebildet, um ein Slicepredicate definieren zu können. Sonst würden<br />
alle Subspalten geladen werden <strong>und</strong> anschließend gefiltert. Dies liegt daran, dass<br />
es kein SubSlicepredicate gibt. Es ist daher unvorteilhafter einen Zeilenschlüssel <strong>und</strong><br />
31
4.2 Auswerten der Anfragen<br />
Triple Pattern Index<br />
(s, p, o) SPO, POS, OSP<br />
(s, p, ?) OSP<br />
(?, p, o) SPO<br />
(s, ?, o) POS<br />
(?, p, ?) SPO<br />
(s, ?, ?) OSP<br />
(?, ?, o) POS<br />
(?, ?, ?) nicht unterstützt<br />
Tabelle 4.1: Tripelmuster <strong>mit</strong> zu verwendender Spaltenfamilie für ColumnFamily-<br />
Inputformat beim ersten Verb<strong>und</strong><br />
Subspaltennamen zu definieren <strong>und</strong> den Superspaltennamen <strong>und</strong>efiniert zu lassen.<br />
Da die Zwischenergebnisse vom HDFS gelesen werden, sodass eine gute Skalierung<br />
gegeben ist, gilt diese Optimierung nur für den ersten Verb<strong>und</strong>. Alle weiteren Zugriffe<br />
auf Cassandra via Hector Client erfolgen nach dem Schema, das in [14] vorgestellt<br />
wird.<br />
32
5 Evaluation<br />
In diesem Kapitel wird geprüft, wie sich die Implementierung bei unterschiedlichen<br />
Datensätzen verhält. Es soll überprüft werden, ob die Kombination aus Hadoop <strong>und</strong><br />
Cassandra effizient im Hinblick auf die Laufzeit ist. Dafür werden die Laufzeiten verglichen,<br />
die sich bei der Anwendung auf unterschiedlichen Datensatzgrößen ergeben.<br />
Außerdem werden verschiedene Cassandra-spezifische Einstellungen variiert, um deren<br />
Einfluss auf die Laufzeit zu untersuchen. Es werden zunächst die Rahmenbedingen<br />
der Evaluation betrachtet, anschließend werden die Laufzeiten präsentiert <strong>und</strong><br />
interpretiert. Abschließend werden Probleme aufgezeigt, die während der Evaluation<br />
auftraten <strong>und</strong> mögliche Ursachen benannt.<br />
5.1 Allgemeine Rahmenbedingunen<br />
Zur Evaluation wird ein Cluster bestehend aus 10 Servern verwendet. Jeder Server<br />
hat einen Intel Xeon Prozessor 3,16 GHZ, 4GB Arbeitsspeicher <strong>und</strong> 3 TeraByte<br />
Festplattenkapazität. Die einzelnen Server, fortan Knoten genannt, kommunizieren<br />
über ein Gigabit-Lan <strong>mit</strong>einander. Das Betriebssystem ist die 64-Bit Version der<br />
Ubuntu-Distribution in der Version 9.10. Als <strong>MapReduce</strong>-Framework wurde „Cloudera’s<br />
Distribution for Hadoop 3“ (CDH3) 1 verwendet. Java ist in Version 1.6.0_26<br />
installiert. Cassandra ist zur Zeit der <strong>Auswertung</strong> in Version 0.8.2 installiert. Einer<br />
der Knoten fungiert ausschließlich als Jobtracker <strong>und</strong> Namenode <strong>und</strong> ist so<strong>mit</strong> an<br />
der Berechnung nicht direkt beteiligt. Die Datenbank läuft ebenfalls nur auf 9 der<br />
10 Knoten, um Störungen <strong>mit</strong> dem Hadoop Jobtracker zu vermeiden.<br />
Die Gr<strong>und</strong>lage der Evaluation bilden Daten, die <strong>mit</strong> dem Benchmark SP 2 Bench generiert<br />
worden sind. Der Benchmark generiert Semantic Web Daten im <strong>RDF</strong>-Format,<br />
die auf der Verteilung echter Daten basieren. Als Referenz dient die Literaturdatenbank<br />
DBLP. Als Benchmark für SPARQL-Anfragen entwickelt, stellt SP 2 Bench<br />
1 http://www.cloudera.com/hadoop/<br />
33
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
außerdem ausgewählte Anfragen zum Testen bereit [16]. Es wurden insgesamt Daten<br />
zwischen 1 Millionen <strong>und</strong> 50 Millionen Tripel evaluiert. Größere Datensätze<br />
konnten leider aufgr<strong>und</strong> wiederholter Ausfälle des Clusters nicht eingespielt werden.<br />
Die Ausfälle sind höchstwahrscheinlich auf eine suboptimale Konfiguration der Speicherzuweisung<br />
zurückzuführen. Aus zeitlichen Gründen war eine Optimierung der<br />
entsprechenden Konfigurationsparameter leider nicht möglich. Allerdings ist da<strong>von</strong><br />
auszugehen, dass weitere Optimierungen die Ausfälle beheben würden.<br />
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
Die Zeitmessung erfolgt <strong>mit</strong> dem Java-Befehl System.nanoTime(). Dieser wird als<br />
erster <strong>und</strong> letzter Befehl des Programms aufgerufen. Insgesamt wurden 4 Anfragen<br />
an das System gerichtet, die auf den <strong>von</strong> SP 2 Bench bereitgestellten Anfragen basieren.<br />
Es wurden eine kurze Anfrage (Q10), zwei Anfragen <strong>mit</strong>tlerer Länge (Q1, Q3a)<br />
<strong>und</strong> eine lange Anfrage (Q2) ausgewählt. Bei einigen Anfragen mussten Teile der<br />
Anfrage weggelassen oder umgeschrieben werden, da es sich nicht bei allen Anfragen<br />
um reine BGP-Anfragen handelt. Der Replikationsfaktor der Cassandra-Datenbank<br />
wurde auf 3 gesetzt. Dies entspricht dem Replikationsfaktor des HDFS.<br />
5.2.1 Q1<br />
SELECT *<br />
WHERE {<br />
}<br />
?journal rdf:type bench:Journal .<br />
?journal dc:title "Journal 1 (1940)"^^xsd:string .<br />
?journal dcterms:issued ?yr .<br />
Listing 5.1: Anfrage Q1<br />
Die Anfrage Q1 aus Listing 5.1 besteht aus drei Tripelmustern <strong>und</strong> wird <strong>mit</strong> zwei<br />
Verbünden ausgewertet. Die Laufzeiten (Tab. 5.1, Abb. 5.1) wachsen wie erwartet<br />
annähernd linear für unterschiedliche Datensatzgrößen.<br />
34
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
Q1 1M 5M 10M 25M 50M<br />
Laufzeit 00:00:19 00:00:36 00:00:40 00:01:00 00:01:54<br />
Ergebnisse 1 1 1 1 1<br />
Tabelle 5.1: <strong>Auswertung</strong> der Anfrage Q1<br />
00:02:01<br />
00:01:44<br />
00:01:26<br />
Zeit (hh:mm:ss)<br />
00:01:09<br />
00:00:52<br />
00:00:35<br />
00:00:17<br />
00:00:00<br />
0 10 20 30 40 50 60<br />
<strong>RDF</strong> Tripel( in Millionen)<br />
Abbildung 5.1: <strong>Auswertung</strong> der Anfrage Q1<br />
35
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
5.2.2 Q2<br />
SELECT *<br />
WHERE {<br />
}<br />
?inproc rdf:type bench:Inproceedings.<br />
?inproc dc:creator ?author.<br />
?inproc bench:booktitle ?booktitle.<br />
?inproc dc:title ?title.<br />
?inproc dcterms:partOf ?proc. ?inproc rdfs:seeAlso ?ee.<br />
?inproc swrc:pages ?page. ?inproc foaf:homepage ?url .<br />
?inproc dcterms:issued ?yr.<br />
Listing 5.2: Anfrage Q2<br />
Die Anfrage Q2, die in Listing 5.2 aufgeführt wird, ist die längste Anfrage <strong>und</strong> besteht<br />
aus insgesamt 8 Verbünden. Es handelt sich hierbei nicht um die ursprüngliche<br />
Anfrage <strong>von</strong> SP 2 Bench, da „OPTIONAL“- <strong>und</strong> „ORDER BY“-Befehle weggelassen<br />
werden mussten.<br />
Q2 1M 5M 10M 25M 50M<br />
Laufzeit 00:02:31 00:06:26 00:14:20 00:51:20 01:48:29<br />
Ergebnisse 64679 252754 613729 1896297 4230949<br />
Tabelle 5.2: <strong>Auswertung</strong> der Anfrage Q2<br />
Die Laufzeiten, dargestellt in Tab. 5.2 <strong>und</strong> Abb. 5.2 sind deutlich länger verglichen<br />
<strong>mit</strong> den anderen Anfragen. Auch bei dieser Anfrage kann ein linearer Verlauf der<br />
Laufzeiten festgestellt werden, wobei das Maximum bei 01:48:29 liegt.<br />
5.2.3 Q3A<br />
Die Anfrage Q3A (Listing 5.3) ist ähnlich zur Anfrage Q1 <strong>und</strong> besteht aus einem<br />
Verb<strong>und</strong>. Die Anfrage Q3A, die <strong>von</strong> SP2Bench bereitsgestellt wird, sah einen<br />
„FILTER“-Befehl vor. Dies wurde vermieden, indem der Wert als Prädikat des<br />
zweiten Tripelmusters eingefügt worden ist. Die Laufzeiten werden in Tab. 5.3 <strong>und</strong><br />
Abb. 5.3 tabellarisch <strong>und</strong> graphisch dargestellt. Auch hier ist eine Linearität der<br />
Laufzeit in Abhängigkeit der Datengröße zu erkennen.<br />
36
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
01:55:12<br />
01:40:48<br />
01:26:24<br />
Zeit (hh:mm:ss)<br />
01:12:00<br />
00:57:36<br />
00:43:12<br />
00:28:48<br />
00:14:24<br />
00:00:00<br />
0 10 20 30 40 50 60<br />
<strong>RDF</strong>-Tripel (in Millionen)<br />
Abbildung 5.2: <strong>Auswertung</strong> der Anfrage Q2<br />
SELECT *<br />
WHERE {<br />
}<br />
?article rdf:type bench:Article .<br />
?article swrc:pages ?value .<br />
Listing 5.3: Anfrage Q3A<br />
Q3 1M 5M 10M 25M 50M<br />
Laufzeit 00:00:18 00:00:38 00:00:58 00:02:15 00:03:41<br />
Ergebnisse 52406 193786 323456 598936 929547<br />
Tabelle 5.3: <strong>Auswertung</strong> der Anfrage Q3A<br />
37
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
00:04:19<br />
00:03:36<br />
Zeit (hh:mm:ss)<br />
00:02:53<br />
00:02:10<br />
00:01:26<br />
00:00:43<br />
00:00:00<br />
0 10 20 30 40 50 60<br />
<strong>RDF</strong>-Tripel (in Millionen)<br />
Abbildung 5.3: <strong>Auswertung</strong> der Anfrage Q3A<br />
5.2.4 Q10<br />
SELECT *<br />
WHERE { ?subject ?predicate person:Paul_Erdoes. }<br />
Listing 5.4: Anfrage Q10<br />
Q10 1M 5M 10M 25M 50M<br />
Laufzeit 00:00:12 00:00:43 00:00:59 00:01:55 00:02:45<br />
Ergebnisse 572 656 656 656 656<br />
Tabelle 5.4: <strong>Auswertung</strong> der Anfrage Q10<br />
Q10, dargestellt in Listing 5.4, ist die kleinste der betrachteten Anfragen <strong>und</strong> besteht<br />
lediglich aus einem Tripelmuster. So<strong>mit</strong> werden die Daten in der Map-Phase<br />
eingelesen <strong>und</strong> direkt auf das HDFS geschrieben. Für die ersten beiden Datensätze<br />
lieferte das Programm das richtige Ergebnis <strong>und</strong> terminierte. Bei größeren Datensätzen<br />
blieb die Map-Phase bei 99% stehen <strong>und</strong> wurde nach wenigen Minuten<br />
abgebrochen, jedoch waren die Ergebnisse korrekt. Die in Tab. 5.4 <strong>und</strong> Abb. 5.4 aufgeführten<br />
Laufzeiten beziehen sich bei diesem Datensatz auf die Zeit, bei der das<br />
Programm 99% erreicht hat. Insgesamt ist auch hier eine Linearität der Laufzeit <strong>mit</strong><br />
steigender Datensatzgröße zu erkennen. Allerdings machte sich die Instabilität des<br />
Systems bemerkbar.<br />
38
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
00:03:36<br />
00:02:53<br />
Zeit in hh:mm:ss<br />
00:02:10<br />
00:01:26<br />
00:00:43<br />
00:00:00<br />
0 10 20 30 40 50 60<br />
<strong>RDF</strong>-Tripel (in Millionen)<br />
Abbildung 5.4: <strong>Auswertung</strong> der Anfrage Q10<br />
5.2.5 Einfluss des Konsistenzgrads<br />
Nachdem unterschiedliche Datensatzgrößen betrachtet wurden, wurden unterschiedliche<br />
Konsistenzgrade bei den Anfragen betrachtet. Der Hector Client geht standardmäßig<br />
zum Lesen vom Konsistenzgrad „QUORUM“ aus. Für den größten Datensatz<br />
(50M ) wurden die ersten drei Anfragen <strong>mit</strong> den Konsistenzgraden „ONE“<br />
<strong>und</strong> „ALL“ wiederholt. Die gemessenen Laufzeiten, in Tabelle Tab. 5.5 aufgeführt,<br />
weisen nur geringe Änderungen auf, die vernachlässigt werden können, da sie auf<br />
Schwankungen der Auslastung des Clusters zurückzuführen sind. Folglich scheint<br />
eine Änderung des Konsistenzgrads bei den gewählten Anfragen keinen Einfluss auf<br />
die Laufzeit zu haben.<br />
50M ONE QUORUM ALL<br />
Q1 00:02:05 00:02:05 00:02:04<br />
Q2 01:41:58 01:48:42 01:40:37<br />
Q3A 00:03:48 00:03:31 00:03:51<br />
Tabelle 5.5: Anfragen <strong>mit</strong> unterschiedlichem Konsistenzgrad<br />
39
5.2 Laufzeiten <strong>und</strong> Beobachtungen<br />
5.2.6 Einfluss des Replikationsfaktors<br />
Zunächst wurde versucht, den Replikationsfaktor schrittweise zu erhöhen. Nachdem<br />
der Replikationsfaktor für den Keyspace 10M auf 4 gesetzt wurde, musste nach<br />
Anleitung ein Repair-Vorgang aufgerufen werden. Nach einigen Minuten war der<br />
Repair-Vorgang abgeschlossen, jedoch gaben die Anfragen fehlerhafte Ergebnisse<br />
aus. Es war zu beobachten, dass bei jedem Programmaufruf die Anzahl eingelesener<br />
Rekords schwankte. Auch nach mehreren St<strong>und</strong>en Wartens, einem Cleanup-Vorgang<br />
<strong>und</strong> einem weiteren Repair-Vorgang, gab es keine Verbesserung. Daher wurde ein<br />
weiterer Keyspace <strong>mit</strong> einem Replikationsfaktor <strong>von</strong> 6 erstellt <strong>und</strong> die Daten erneut<br />
eingespielt. Die Map-Tasks griffen wie erwartet auf sechs verschiedene Knoten<br />
zu, um die Daten zu laden. Die Ergebnisse waren korrekt, die Laufzeiten (Abb. 5.5,<br />
Tab. 5.6) zeigten jedoch keine Verbesserung, sondern waren durchschnittlich um ca.<br />
50% erhöht. Da <strong>mit</strong> einem höheren Replikationsfaktor mehr Kopien der Daten im<br />
Cluster vorhanden sind, wurden kürzere Zugriffszeiten <strong>und</strong> so<strong>mit</strong> kürzere Gesamtlaufzeiten<br />
erwartet.<br />
02:52:48<br />
02:24:00<br />
Zeit (hh:mm:ss)<br />
01:55:12<br />
01:26:24<br />
00:57:36<br />
50M RF 3<br />
50M RF 6<br />
00:28:48<br />
00:00:00<br />
Q1 Q2 Q3A<br />
Anfrage<br />
Abbildung 5.5: Vergleich der Laufzeiten bei unterschiedlichem Replikationsfaktor<br />
bei 50 Millionen Tripel<br />
40
5.3 Probleme <strong>und</strong> mögliche Ursachen<br />
Q1 Q2 Q3A<br />
10M RF 3 00:00:40 00:14:20 00:00:58<br />
10M RF 6 00:01:20 00:22:13 00:02:07<br />
25M RF 3 00:01:00 00:51:20 00:02:15<br />
25M RF 6 00:02:51 01:13:56 00:04:11<br />
50M RF 3 00:01:54 01:48:29 00:03:41<br />
50M RF 6 00:05:42 02:35:52 00:07:43<br />
Tabelle 5.6: Anfragen <strong>mit</strong> unterschiedlichem Replikationsfaktor<br />
5.2.7 Datenverteilung<br />
Die Verteilung der Daten erfolgt trotz Verwendung des RandomPartitioner nicht<br />
ganz gleichmäßig. Abb. 5.6 zeigt die neun Knoten, auf denen Cassandra installiert<br />
ist, sowie die Größe der gespeicherten Daten. Die Größen variieren <strong>von</strong> 10,69 GB<br />
bis 16,61 GB.<br />
Abbildung 5.6: Auszug aus der Clusterübersicht (Bildschirmaufnahme)<br />
5.3 Probleme <strong>und</strong> mögliche Ursachen<br />
Bei der Evaluation traten verschiedene Probleme auf, die sowohl das Einlesen als<br />
auch das Auswerten der Daten betrafen. Beim Einlesen der Datensätze <strong>mit</strong> 50 Millionen<br />
Tripeln oder größer war nach einigen St<strong>und</strong>en mehrere Knoten nicht verfügbar<br />
<strong>und</strong> das Einlesen war fehlgeschlagen. Auch nach einem Neustart der Systeme stürzten<br />
einige wieder ab. Erst das Löschen des zum Datensatz gehörenden Keyspace<br />
löste das Problem. Nach Auswerten der log-Dateien wurde ein Überlaufen des Java-<br />
Heap-Space festgestellt. Cassandra schreibt die Daten zunächst in eine Datenstruktur<br />
MemTable, die im Speicher liegt. Erreicht die Tabelle eine in der Konfiguration<br />
41
5.3 Probleme <strong>und</strong> mögliche Ursachen<br />
festgelegte Größe, werden die Daten auf die Festplatte geschrieben. Um die Auslastung<br />
der Datenbank zu senken, wurde die einzulesende Datei zunächst in Dateien<br />
<strong>mit</strong> 250.000 Zeilen Länge aufgeteilt. Die aufgeteilten Dateien wurden <strong>mit</strong> jeweils 3<br />
Minuten Pause zwischen den einzelnen Teilen eingelesen, um der Datenbank mehr<br />
Zeit zum Leeren der internen Datenstrukturen zu geben. Da dies immer noch zu<br />
Problemen führte, wurde das Einleseprogramm durch Einfügen <strong>von</strong> Sleep-Befehlen<br />
verändert, sodass das Einlesen künstlich verlangsamt wurde. Durch diese Änderungen<br />
gelang es, den Datensatz bestehend aus 50 Millionen Tripel einzulesen. Allerdings<br />
war es nicht möglich, größere Datensätze in akzeptabler Zeit einzulesen. Da ein<br />
Absturz immer <strong>mit</strong> dem Neustart des gesamten Clusters verb<strong>und</strong>en war, gestaltete<br />
sich die Problemsuche <strong>und</strong> das Testen verschiedener Einleseparameter schwierig.<br />
42
6 Verwandte Arbeiten<br />
In diesem Kapitel werden Arbeiten <strong>und</strong> Projekte, die <strong>mit</strong> dieser Arbeit verwandt<br />
sind vorgestellt. Dabei werden die Gemeinsamkeiten <strong>und</strong> Unterschiede aufgezeigt.<br />
Das Heart-Projekt 1<br />
(Highly Extensible & Accumulative <strong>RDF</strong> Table) kombiniert<br />
Apache Hadoop <strong>und</strong> die NoSQL-Datenbank HBase zur effizienten Speicherung <strong>und</strong><br />
<strong>Auswertung</strong> <strong>von</strong> <strong>RDF</strong>-Daten. Ähnlich zur der in dieser Arbeit vorgestellten Implementierung<br />
werden die Daten in HBase so abgespeichert, dass Verbünde berechnet<br />
werden können. Das Projekt befindet sich im Anfangsstadium <strong>und</strong> scheint noch keine<br />
Implementierung aufzuweisen.<br />
Die Kombination aus Hadoop <strong>und</strong> HBase lässt sich ebenfalls in [2] wiederfinden.<br />
Die Autoren Sun <strong>und</strong> Jin präsentieren einen skalierbaren <strong>RDF</strong>-Store, der HBase zur<br />
Speicherung der Daten verwendet. Die Daten werden ähnlich zu der in Kap. 3.1 vorgestellten<br />
Speicherstrategie mehrfach abgespeichert, um eine effiziente <strong>Auswertung</strong><br />
aller Tripelmuster zu gewährleisten. Außerdem wird ein Greeedy-Algorithmus zur<br />
<strong>Auswertung</strong> <strong>von</strong> SPARQL Basic Graph Pattern <strong>mit</strong> Hadoop vorgestellt.<br />
Der in [14] beschriebene <strong>RDF</strong>-Triple-Store Cumulus<strong>RDF</strong> speichert <strong>RDF</strong>-Daten in<br />
Cassandra, um effizientes Nachschlagen einzelner Tripelmuster zu gewährleisten . Eine<br />
<strong>Auswertung</strong> komplexerer Anfragen, wie etwa SPARQL-BGP-Anfragen, ist jedoch<br />
nicht möglich. Die Speicherstrategie <strong>von</strong> Cumulus<strong>RDF</strong> wurde in der vorgestellten<br />
Implementierung übernommen <strong>und</strong> zur <strong>Auswertung</strong> angepasst. Eine konkrete Implementierung<br />
des beschrieben <strong>RDF</strong>-Triple-Stores ist vorhanden <strong>und</strong> frei zugänglich 2 .<br />
Ein weiteres Projekt, das sich <strong>mit</strong> der Speicherung <strong>von</strong> <strong>RDF</strong>-Daten <strong>mit</strong> Cassandra<br />
befasst, ist rdf-cassandra 3 . Das Projekt stellt einen StorageAdapter für die Programmiersprache<br />
Ruby bereit. Die Daten werden nur einfach abgespeichert <strong>und</strong> entsprechen<br />
demnach ungefähr dem Schema der Spaltenfamilie „SPO“ in der vorliegenden<br />
1 http://wiki.apache.org/incubator/HeartProposal<br />
2 http://code.google.com/p/cumulusrdf/<br />
3 https://github.com/bendiken/rdf-cassandra<br />
43
Verwandte Arbeiten<br />
Implementierung.<br />
Die <strong>von</strong> dem Unternehmen Datastax entwickelte Hadoop-Distribution Brisk 4 kombiniert<br />
Hadoop <strong>und</strong> Cassandra. Es handelt sich dabei zwar nicht um einen <strong>RDF</strong>-<br />
Triple-Store, jedoch wird auch hier das Potenzial der Kombination beider Technologien<br />
deutlich. Das HDFS wird durch ein auf Cassandra basierendes Dateisystem,<br />
CassandraFS, ersetzt. Die Distribution stellt außerdem die Kernfunktionalitäten<br />
<strong>MapReduce</strong>, Hive, sowie Job- <strong>und</strong> Tasktracker zur Verfügung. Aus der Kombination<br />
ergibt sich die Möglichkeit sowohl Echtzeit-Anwendungen zu realisieren als auch die<br />
zeitintensivere Analyse <strong>mit</strong> <strong>MapReduce</strong> durchzuführen.<br />
4 http://www.datastax.com/products/brisk<br />
44
7 Zusammenfassung<br />
Die vorliegende Arbeit untersuchte, ob eine effiziente Speicherung <strong>und</strong> <strong>Auswertung</strong><br />
<strong>von</strong> <strong>RDF</strong>-Daten <strong>mit</strong> der Kombination aus dem quelloffenen Apache Hadoop Framework<br />
<strong>und</strong> <strong>und</strong> der NoSQL-Datenbank Apache Cassandra möglich ist. Das hierfür<br />
entwickelte System ermöglicht die Speicherung <strong>von</strong> <strong>RDF</strong>-Daten, sowie die <strong>Auswertung</strong><br />
<strong>von</strong> SPARQL Basic Graph Pattern-Anfragen auf diesen Daten.<br />
Um einen schnellen Zugriff auf die gewünschten <strong>RDF</strong>-Daten zu erhalten, werden die<br />
<strong>RDF</strong>-Daten dreimal in verschiedene Spaltenfamilien abgespeichert. Bei der <strong>Auswertung</strong><br />
wird die Anfrage zunächst in Tripelmuster unterteilt. Anschließend wird eine<br />
Sequenz <strong>von</strong> <strong>MapReduce</strong>-Jobs erzeugt, die Verbünde zwischen den Tripelmustern<br />
berechnen. Der Verb<strong>und</strong> selbst ist ein Map-Side-Join, bei dem die Daten während<br />
der Map-Phase für jedes Tripel dynamisch geladen <strong>und</strong> verb<strong>und</strong>en werden. Die Zwischenergebnisse<br />
<strong>und</strong> das Endergebnis werden auf das verteilte Dateisystem HDFS<br />
geschrieben.<br />
Die Evaluation der Anwendung <strong>mit</strong> dem SPARQL-Benchmark SP 2 Bench zeigte einerseits<br />
vielversprechende Laufzeiten der Anwendung auf größeren Datensätze, andererseits<br />
Probleme bei der Ausführung auf dem Cluster.<br />
Zusammenfassend kann die Implementierung als „Proof-of-Concept“ betrachtet werden,<br />
dessen Erkenntnisse bei der Implementierung als Gr<strong>und</strong>lage weiterer Projekte<br />
in diesem Bereich dienen können.<br />
7.1 Ausblick<br />
Über die Implementierung der Anwendung <strong>und</strong> der vorliegenden Arbeit hinausgehend<br />
sollten verschiedene Einstellungen bei der Konfiguration <strong>von</strong> Hadoop <strong>und</strong> Cassandra<br />
betrachtet werden. Insbesondere der parallele Einsatz im Rechner-Cluster<br />
45
7.1 Ausblick<br />
kann weiter untersucht <strong>und</strong> dokumentiert werden. Ebenfalls ist es vorstellbar verschiedene<br />
Joins zu implementieren <strong>und</strong> deren Effizienz zu untersuchen, was aufgr<strong>und</strong><br />
der aufgeführten Li<strong>mit</strong>ierung seitens des ColumnFamilyInputFormat noch<br />
nicht möglich ist. Die Implementierung unterstützt nur einen Teil des SPARQL-<br />
Standards <strong>und</strong> könnte zur Unterstützung des gesamten SPARQL-Standards erweitert<br />
werden. Außerdem hat die Reihenfolge der Tripelmuster einen großen Einfluss<br />
auf die Laufzeit der Verbünde <strong>und</strong> bietet so<strong>mit</strong> Möglichkeiten der Optimierung. Abschließend<br />
könnte das Gesamtsystem <strong>mit</strong> einem ähnlichen System, z.B. auf HBase<br />
aufbauend, verglichen werden, um die Gesamtleistung einordnen zu können.<br />
46
Literaturverzeichnis<br />
[1] T. Berners-Lee, J. Hendler, and O. Lassila, “The Semantic Web (Berners-Lee<br />
et. al 2001),” May 2001.<br />
[2] J. Sun and Q. Jin, “Scalable rdf store based on hbase and mapreduce,” in Advanced<br />
Computer Theory and Engineering (ICACTE), 2010 3rd International<br />
Conference on, vol. 1. IEEE, 2010, pp. V1–633.<br />
[3] N. Shadbolt, W. Hall, and T. Berners-Lee, “The semantic web revisited,” Intelligent<br />
Systems, IEEE, vol. 21, no. 3, pp. 96 –101, jan.-feb. 2006.<br />
[4] P. Hitzler, M. Krötzsch, S. Rudolph, and Y. Sure, Semantic Web: Gr<strong>und</strong>lagen.<br />
Springer, 2008.<br />
[5] F. Manola and E. Miller, Eds., <strong>RDF</strong> Primer, ser. W3C Recommendation.<br />
World Wide Web Consortium, Feb. 2004.<br />
[6] E. Prud’hommeaux and A. Seaborne, Eds., SPARQL Query Language for <strong>RDF</strong>,<br />
ser. W3C Recommendation. World Wide Web Consortium, Jan. 2008.<br />
[7] S. Ghemawat, H. Gobioff, and S.-T. Leung, “The google file system,” in SOSP,<br />
M. L. Scott and L. L. Peterson, Eds. ACM, 2003, pp. 29–43.<br />
[8] J. Dean and S. Ghemawat, “Mapreduce: a flexible data processing tool,” Commun.<br />
ACM, vol. 53, no. 1, pp. 72–77, 2010.<br />
[9] T. White, Hadoop: The Definitive Guide; 2nd rev. ed. O’Reilly, 2011.<br />
[10] “Module 4: Mapreduce,” http://developer.yahoo.com/hadoop/tutorial/<br />
module4.html, Online, letzter Aufruf 03.08.2011.<br />
[11] E. Hewitt, Cassandra: The Definitive Guide. O’Reilly Media, 2010.<br />
[12] R. Cattell, “Scalable sql and nosql data stores,” 2010.<br />
[13] “Cassandra wiki api,” http://wiki.apache.org/cassandra/API/, Online, letzter<br />
Aufruf 03.08.2011.<br />
47
[14] G. Ladwig and A. Harth, “An rdf storage scheme on key-value stores for linked<br />
data publishing,” Tech. Rep., 2010.<br />
[15] J. Lin and C. Dyer, “Data-intensive text processing with mapreduce,” vol. 3,<br />
no. 1, pp. 1–177, 2010.<br />
[16] M. Schmidt, T. Hornung, G. Lausen, and C. Pinkel, “Sp2bench: A sparql performance<br />
benchmark,” 2008.
Erklärung<br />
Hier<strong>mit</strong> erkläre ich, dass ich diese Abschlussarbeit selbständig verfasst habe, keine<br />
anderen als die angegebenen Quellen/Hilfs<strong>mit</strong>tel verwendet habe <strong>und</strong> alle Stellen,<br />
die wörtlich oder sinngemäß aus veröffentlichten Schriften entnommen wurden, als<br />
solche kenntlich gemacht habe. Darüber hinaus erkläre ich, dass diese Abschlussarbeit<br />
nicht, auch nicht auszugsweise, bereits für eine andere Prüfung angefertigt<br />
wurde.<br />
Ort, Datum<br />
Unterschrift