Verarbeiten großer verteilter Datenmengen mit Hadoop
Internetfirmen müssen für den Betrieb ihrer Dienste täglich Datenmengen im Giga- und Petabereich speichern und effizient verarbeiten. Den Part der Datenverarbeitung können Googles MapReduce oder die Open-Source-Nachbildung Hadoop übernehmen.
- Oliver Fischer
Internetfirmen müssen für den Betrieb ihrer Dienste täglich Datenmengen im Giga- und Petabereich speichern und effizient verarbeiten. Den Part der Datenverarbeitung können Googles MapReduce oder die Open-Source-Nachbildung Hadoop übernehmen, indem sie die Eingabedaten in Teilstücke partitionieren, um sie anschließend als unabhängige Teilaufgaben bearbeiten zu können.
Ein architektonischer Ansatz zur Bewältigung großer Datenmengen ist das verteilte Rechnen in Clustern, wobei kleinere schwache Systeme eine Aufgabe gleichzeitig angehen, anstatt sie auf einem einzelnen großen System sequenziell zu bearbeiten.
Firmen wie Facebook, Google und Yahoo stehen für den Betrieb ihrer Dienste genau vor der Aufgabe, täglich Datenmengen im Giga- und Petabereich speichern und effizient verarbeiten zu können. Bei Google führte das zur Entwicklung des Google File Sytem (GFS), eines nach dem Master-Slave-Prinzip arbeitenden verteilten Cluster-Dateisystems. Eine der Hauptanforderungen an das GFS war die Fehlertoleranz bei Hardwareausfällen, da das GFS auf Standardhardware betrieben werden sollte. Diese Anforderung erfüllte Google durch Replikation.
Damit hatte Google einen Teil der Aufgabe gelöst: die Datenspeicherung. Offen blieb damit nur die Verarbeitung. Herkömmlicherweise werden die zu verarbeitenden Daten zum Programm gebracht. Das Programm startet auf dem einen Rechner und holt sich die benötigten Daten aus einer anderen Quelle wie einer Datenbank. Dieser Weg war für Google bei den zu verarbeitenden Datenmengen nicht gangbar, denn der Netzdurchsatz für den Datentransfer erwies sich als Flaschenhals für die gesamte Verarbeitung. Daher muss die Verarbeitung der Daten dort stattfinden, wo die Daten gespeichert wurden. Das Prinzip wird als Data Locality bezeichnet.
Google entwickelte hierfür das MapReduce-Programmiermodell, das die Daten in kleine Teile aufteilt und zur gleichzeitigen Verarbeitung auf unterschiedliche Rechner verteilt. Das Endergebnis entsteht durch die Zusammenführung aller Teilergebnisse. MapReduce bot damit eine Lösung zur Verarbeitung verteilter Daten und ermöglichte eine gute Integration mit dem GFS. Google stellte beide, MapReduce und das Google File System, 2003 und 2004, vor und öffnete damit den Weg für Open-Source-Implementierungen. Seine eigenen Implementierungen stellte Google nicht zur Verfügung.
Doug Cutting implementierte MapReduce 2005 für Nutch und legte damit die Grundlage für das quelloffene Hadoop-Projekt, eine Implementierung von GFS und MapReduce. Nachdem Hadoop 2006 zuerst als Lucene-Subprojekt startete, erhielt es 2008 die Weihen eines Top-Level-Projekts der Apache Software Foundation (ASF) und beherbergt heute eigene Subprojekte. Im Juli 2009 gewann ein Cluster mit dem Hadoop-Framework zweimal den Terabyte Sort Benchmark, der in unterschiedlichen Kategorien die leistungsfähigsten Systeme zur Datensortierung bestimmt. Ein Hadoop-Cluster von Yahoo konnte 100 Terabyte in zwei Stunden und 53 Minuten sortieren.
Architektur und Installation
Im Lauf der Zeit wurde das Hadoop-Projekt mehrfach umstrukturiert, um der fortschreitenden Entwicklung Rechnung zu tragen. Hadoop besteht heute im Wesentlichen aus den drei Komponenten Hadoop Common, Hadoop Distributed File System (HDFS) sowie MapReduce und arbeitet nach dem Master-Slave-Prinzip. Ein Knoten im Cluster übernimmt die Rolle des Masters, der die Arbeit der restlichen Knoten koordiniert.
Hadoop Common stellt die Grundfunktionen bereit, die alle anderen Komponenten benötigen. Dazu zählen eine implementierungsneutrale Filesystem-Schnittstelle, die Schnittstelle für die "Remote Procedure Call"-Kommunikation im Cluster und Bibliotheken für die Serialisierung von Daten. Das HDFS ist das Standarddateisystem des Hadoop-Frameworks. MapReduce bietet alle Funktionen, um nach dem Programmiermodell zu entwickeln. Neben HDFS lassen sich andere Systeme für die Speicherung der Daten einsetzen, darunter CloudStore und Amazons S3.
Mit den drei Komponenten kann man einen vollständigen Hadoop-Cluster aufbauen. Als Produktionsbetriebssystem für die einzelnen Knoten eines Hadoop-Clusters wird in der Hadoop-Dokumentation Linux empfohlen, wobei sich Hadoop-Cluster auch auf Basis anderer Unix-artiger Systeme wie FreeBSD oder Solaris aufbauen lassen. Für die gewählte Plattform muss Suns JDK ab Version 1.6 verfügbar sein. Der Betrieb mit OpenJDK ist nicht möglich. Über die Projekt-Website lässt sich das derzeit aktuelle Release 0.20.2 herunterladen. Hadoop-Nutzer wie Yahoo, A9 (Amazons Suchmaschine) oder last.fm betreiben Hadoop-Cluster in Größenordnungen von 10 bis zu mehreren tausend Knoten.
Yahoos größte Installation umfasst beispielsweise 4000 Knoten. Daher ist es schwer, eine Empfehlung für das Dimensionieren eines Hadoop-Clusters zu geben, da das wesentlich von der geplanten Verwendung abhängt. Ausnahme ist das Größenverhältnis des Master-Knotens zu den restlichen Knoten der Cluster: der Masterknoten muss über mehr Leistung und Arbeitsspeicher verfügen als die übrigen Knoten. Das liegt unter anderem daran, dass einzelne Framework-Komponenten, die auf dem Master laufen, aus Performancegründen sämtliche Informationen im Arbeitsspeicher halten und dadurch teure I/O-Operationen vermeiden.
Für erste eigene Schritte unterstützt Hadoop einen pseudoverteilten Modus, bei dem alle Komponenten auf einem einzigen Rechner in einem eigenen Prozess laufen. Hierfür reicht ein normaler Arbeitsplatzrechner wie das eigene Notebook. Damit lassen sich eigene MapReduce-Anwendungen einfach entwickeln und testen, allerdings können in dieser Konfiguration keine Schlussfolgerungen auf die Performance der Anwendung in einem Cluster gezogen werden.
Eine detaillierte Installationsanleitung findet sich auf der Projekt-Website. Die Einrichtung eines echten Clusters dürfte dank der Projektdokumentation für Benutzer mit Unix-Hintergrund keine Hürde darstellen. Die US-Firma Cloudera stellt für die vereinfachte Installation RPM- und Debian-Packages bereit. Für eine erste Begegnung mit Hadoop offeriert sie ein freies VMWare-Image, das lediglich den kostenlosen VMWare Player voraussetzt.
Wer im nächsten Schritt sich für die Entwicklung eigener MapReduce-Anwendungen entschieden hat, aber keinen eigenen Cluster bereitstellen kann oder möchte, kann mit Amazons Elastic MapReduce Hadoop als SaaS-Angebot im Rahmen der Amazon-Webservices nutzen. Mit deren EC2 (Elastic Cloud) lässt sich auch ein individueller Hadoop-Cluster konfigurieren.
Hadoop Distributed File System
Hadoop Distributed File System
HDFS folgt dem Vorbild des Google File System (GFS). Daher stimmt es in wichtigen Eigenschaften, wie sie das von Google veröffentlichte Paper "The Google Filesystem" beschreibt, überein. Google ging bei der Entwicklung des GFS von der Annahme aus, dass in Rechnerclustern der Ausfall einzelner Knoten keine Ausnahme, sondern der Normalfall darstellt, wenn für den Clusteraufbau auf teure Spezial- zugunsten preiswerterer Standardhardware verzichten wird. Daher war Fehlertoleranz gegenüber Hardwareausfällen ein Hauptdesignziel für GFS. Daneben war es für Google notwendig, über ein verteiltes Dateisystem zu verfügen, das dem hohen Datenvolumen gerecht wird und sich dafür leicht durch das Hinzufügen neuer Knoten zum Cluster erweitern lässt.
Im Rahmen des Master-Slave-Prinzips übernimmt die Rolle des Masters beim HDFS der NameNode, der alle Metadaten des Filesystems wie Verzeichnisstrukturen und Dateien verwaltet. Die sogenannten DataNodes übernehmen die Rolle der Slaves und sind für die Verwaltung der Nutzdaten im HDFS auf den einzelnen Clusterknoten zuständig.
Für einen Benutzer sieht das HDFS wie ein normales hierarchisches Dateisystem aus, es ist aber von seinen Funktionen her nicht mit anderen verteilten Dateisystemen wie dem Andrew File System vergleichbar, da es für die Entwicklung von MapReduce-Anwendungen optimiert ist. Genauso wie das GFS geht HDFS davon aus, dass Dateien nur einmal geschrieben, dafür aber häufig gelesen werden, sodass Operationen zum Lesen effizienter sein müssen als die zum Schreiben. Eine weitere Besonderheit stellt das Fehlen von Operationen zum bytegenauen Zugriff auf Dateien dar, wie sie unter anderem von der C-Funktion fseek() her bekannt sind.
Mit zunehmender Größe des Clusters wächst auch die Wahrscheinlichkeit eines Hardwareausfalles. Das sollte in GFS/HDFS-Cluster unter normalen Umständen nicht zu Datenverlust führen. Daher darf keine Datei nur auf einem Knoten liegen, dessen alleiniger Ausfall zu Datenverlust führen würde. Die hierfür benötigte Datensicherheit erreicht das HDFS durch Replikation aller Nutzdaten.
Statt jedoch ganze Dateien auf unterschiedliche Knoten zu replizieren, wählten die Entwickler des GFS/HDFS einen blockorientierten Ansatz. Mit dem Ansatz wird jede im HDFS abgelegte Datei in einzelne Blöcke mit einer festen Bytegröße aufgeteilt und durch den NameNode auf unterschiedliche Clusterknoten abgelegt. Zusätzlich wird durch den NameNode sichergestellt, dass jeder Block mehrfach auf unterschiedliche Knoten im Cluster repliziert wird.
In der HDFS-Standardkonfiguration repliziert der NameNode jeden Block dreifach. Kommt es nun zum Ausfall eines Knotens im Cluster, gehen nur die auf dem ausgefallenen Rechners sich befindlichen Blöcke verloren und keine ganze Datei, da die verloren gegangenen Blöcke durch ihre Kopien auf anderen Knoten ersetzbar sind und so der NameNode die ganze Datei bereitstellen kann.
Der NameNode, der alle im HDFS existierenden Verzeichnisse und Dateien kennt, muss daher zu jeder Datei wissen, in welche Blöcke sie aufgeteilt wurde, auf welchem Cluster-Knoten sie liegen und welche Kopien der einzelnen Blöcke es gibt. Zu deren Verwaltung laufen auf dem Cluster-Knoten die sogenannten DataNodes. Im Gegensatz zum NameNode, von denen es nur einen pro HDFS-Instanz gibt, verwalten die DataNodes nur die einzelnen Blöcke, die auf ihrem Knoten abgelegt wurden. Über die Information welche Blöcke zu welcher Datei gehören, verfügt nur der NameNode.
Soll ein Client beispielsweise eine Datei aus dem HDFS lesen, baut er zuerst eine Verbindung zum NameNode auf. Der ermittelt, welche Blöcke zu der angeforderten Datei gehören und auf welchen DataNodes sie liegen. Für den Datentransfer sind nun der anfragende Client und die DataNodes zuständig, da der Datentransfer nicht zur Aufgabe des NameNode gehört. Die Aufteilung stellt sicher, dass der NameNode nicht überlastet wird und sich Transfers dezentral durchführen lassen, während der NameNode für andere Aufgaben zur Verfügung steht.
Ähnlich verhält sich das Prozedere beim Übertragen einer Datei ins HDFS. Der Client teilt dem NameNode mit, dass er eine Datei anlegen soll, worauf der NameNode für die neue Datei einen Eintrag in seinem Namespace erstellt und der Client mit der Datenübertragung anfangen kann. Dafür baut der Client lokal aus den zu übertragenden Daten die Byteblöcke für das HDFS auf. Sobald ein Block seine vorgegebene Größe erreicht hat, fordert der Client beim NameNode Speicherplatz für ihn an. Der NameNode bestimmt dann eine Liste von DataNodes, auf denen der Block mit seinen Kopien abzulegen ist.
Für die eigentliche Übertragung kommt anschließend ein als Pipelining bezeichneter Prozess zum Einsatz. Der Client überträgt den Block zum ersten DataNode aus der erhaltenen Liste. Sobald er den Block erfolgreich auf seinem lokalen System abgelegt hat, transferiert er ihn weiter zum nächsten DataNode aus der Liste. Der Replikationsvorgang wiederholt sich, bis die zu erreichende Anzahl von Blockkopien erreicht ist. Allerdings ist er für den Client transparent, der in der Zwischenzeit den nächsten Block für das HDFS aufbauen und übertragen kann. Der Transfer der gesamten Datei findet erst ein Ende, wenn der NameNode sichergestellt hat, dass jeder Block mit einer Mindestzahl Kopien im HDFS abgelegt wurde.
Bei Größen von bis zu mehreren hundert Knoten in einem HDFS-Cluster stellen Hardwareausfälle ein tägliches Vorkommnis dar, das es jedoch zu dokumentieren gilt. Deshalb schickt jeder DataNode in konfigurierbaren Abständen einen Heartbeat an den zentralen NameNode im HDFS, der sich für jeden DataNode den Zeitpunkt des letzten Heartbeats merkt. Bleibt er über einen längeren Zeitpunkt aus, erklärt der NameNode ihn für tot. Damit man durch den vermeintlichen Ausfall nicht die Mindestanzahl von Kopien pro Block unterschreitet und so die Wahrscheinlichkeit eines Datenverlusts ansteigt, weist der NameNode die DataNodes mit den verbleibenden Kopien an, sie auf andere DataNodes zu replizieren.
Etwas anders sieht es jedoch beim Ausfall des NameNode aus. Ihn gibt es pro HDFS-Instanz nur einmal, weshalb er einen "Single Point of Failure" im HDFS darstellt. Der NameNode speichert seine Informationen über die Metadaten des HDFS im Dateisystem seines Hosts ab. Das Abbild bezeichnet man als FsImage, das beim Start des NameNode vollständig von diesem in den Arbeitsspeicher geladen wird. Dadurch kann der NameNode Anfragen schnell beantworten. Um eine Beschädigung des FsImage durch eventuelle Schreiboperationen bei Änderungen an den Metadaten des HDFS zu vermeiden, werden Änderungen nur im Speicher gehalten und nicht augenblicklich persistiert. Die Aufgabe übernimmt der SecondaryNameNode, der jedem NameNode beigestellt ist. Er protokolliert alle Änderungen im EditLog. Startet ein NameNode, liest er sowohl das letzte FsImage als auch das EditLog ein und führt alle Änderungen aus dem EditLog am FsImage nach. Das aktualisierte FsImage speichert der NameNode, der erst anschließend seine Arbeit aufnimmt.
Neben dem bereitgestellten Befehlszeilen-Client, lässt sich über die Java-API aus jeder Java-Anwendung mit dem HDFS arbeiten.
Programmiermodell und Framework
Programmiermodell und Framework
Mit MapReduce sind oft zwei Dinge gemeint. Zum einen versteht man es als Programmiermodell und zum anderen bezeichnet man damit MapReduce-Frameworks. Letztere arbeiten nach dem MapReduce-Modell, unterscheiden sich aber durch die Wahl der Programmiersprache und in den Implementierungsdetails. Google hat MapReduce als Programmiermodell für die hochparallele Datenverarbeitung in Clustern ausgelegt. Es hat als Konzept seine Wurzeln in Lisp, das die beiden Funktionen map() und reduce() kennt, die auch als Namensgeber für MapReduce fungierten. Lisps map()-Funktion wird mit einer Liste von Eingabewerten und einer Funktion als Parameter aufgerufen, damit es übergebene Funktion auf jeden Wert der Liste anwendet.
Die Ergebnisse der Funktionsaufrufe lassen sich in Listenform als map()-Ergebnis zurückgegeben. reduce() arbeitet ähnlich zu map(), indem man es mit einer Funktion und einer Liste mit Eingabewerten aufruft und jeden Wert aus der Eingabeliste der Funktion übergibt. Im Gegensatz zu map() führt reduce() alle Ergebnisse zusammen und gibt das Ergebnis als skalare Werte zurück.
Das MapReduce-Programmiermodell lehnt sich daran an und unterteilt die Verarbeitung in eine Map- und eine nachfolgende Reduce-Phase, die verteilt auf allen Rechnern eines Clusters ausgeführt werden. Die konkrete Implementierung der Map- und der Reduce-Funktion gibt der Programmierer vor, dem alle Freiheiten offen stehen. Da MapReduce für die Umsetzung in Rechnerclustern ausgelegt wurde, ist von der Annahme auszugehen, dass Worker jede der beiden Phasen auf mehreren Knoten des Clusters parallel ausführen können.
Hierfür instanziiert das darunter liegende Framework eine bestimmte Anzahl Worker mit der vorgegebenen Map-Funktion, teilt die zu verarbeitenden Daten in als Splits bezeichnete Teile auf und weist sie den Workern zu. Jeder Worker führt daraufhin die Map-Funktion auf den ihm zugewiesenen Splits aus. Die Eingabedaten lassen sich durch das Hadoop-Framework als Schlüssel-Wert-Paare aufbereiten und der Map-Funktion übergeben, die sie verarbeitet und dabei eine beliebige Menge von als Intermediate-Werte bezeichneten Ausgabepaaren erzeugen kann. Das folgende Listing stellt das als Pseudocode für das Zählen von Wortlängen in Textdokumenten dar.
;; lineno: Zeilennummer
;; line: Textzeile
map(Key lineno, Value line)
for each word w in line:
EmitIntermediatePair(w, length(w));
Das Framework empfängt die Intermediate-Werte und verteilt sie auf die die Reduce-Funktion ausführenden Worker, wobei das Framework sicherstellt, dass alle Intermediate-Paare mit dem gleichen Schlüsselwert zusammenfasst und an die gleiche Reducer-Instanz übergeben werden. Die Ausgabe der Reduce-Funktion stellt das Ergebnis der MapReduce-Anwendung dar. Ein zentraler Master, der die verfügbaren Worker einer der beiden Phasen zuordnet, koordiniert und überwacht die MapReduce-Anwendungen. Die Abbildung 1 stellt die Verarbeitung und die einzelnen Phasen dar.
Hadoops MapReduce-Framework arbeitet nach dem oben beschriebenen Modell. Die Anwendungen für Hadoop bezeichnet man als Jobs, für deren Verteilung im Cluster und anschließenden Verwaltung der als Master fungierende JobTracker zuständig ist. Die eigentliche MapReduce-Job-Ausführung übernimmt der TaskTracker, von dem je eine Instanz auf einem Clusterknoten läuft. Üblicherweise arbeiten auf einem Knoten immer eine TaskTracker- und eine DataNode-Instanz als Paar, die das Zusammenspiel von HDFS und MapReduce-Framework ermöglichen. Abbildung 2 gibt einen Überblick über die Architektur und Abbildung 3 stellt einen Beispielcluster mit fünf Knoten dar.
Monitoring von Hadoop-Clustern
Zur Überwachung eines Hadoop-Clusters stellen der NameNode für das HDFS und der JobTracker für MapReduce eine spartanische Weboberfläche zur Verfügung, auf der die wichtigsten Parameter zur Verfügung stehen, etwa für HDFS-Angaben zur Speicherkapazität und ihre Auslastung sowie die Anzahl der aktiven Knoten beziehungsweise der ausgefallenen Knoten im Cluster. Die Oberfläche des JobTrackers beschränkt sich ebenfalls auf eine kurze Übersicht der laufenden und auf Ausführung wartenden Jobs, stellt aber für jeden einzelnen Job detaillierte Informationen zum Fortschritt insgesamt, den ausgeführten Tasks und der durch Hadoop verwendeten Parameter bereit. Letzteres stellt eine gute Informationsquelle für spätere Optimierungen dar. Wer einen tieferen Einblick in die inneren Vorgänge in einem Hadoop-Cluster gewinnen möchte, sollte sich die von Hadoop-Framework via log4j geschriebenen Logfiles ansehen, die für jede Serverinstanz des Clusters geschrieben werden.
Zwar hilft das gut, die internen Vorgänge zu verstehen, aber nicht, einen Cluster an sich zu überwachen. Daher unterstützt Hadoop die Monitoring-Lösung Ganglia, aber auch Nagios lässt sich in Verbindung mit Hadoops JMX-Support einsetzen.
Fazit
MapReduce-Ökosystem
Neben Hadoop existieren weitere MapReduce-Implementierungen wie Quizmt, ein von MySpace auf .NET-Basis entwickeltes Framework unter der GPL-3-Lizenz. Einen hybriden Ansatz verfolgt die Greenplum Database, ein kommerzielles Produkt mit SQL- und MapReduce-Unterstützung. Phoenix verfolgt einen Shared-Memory-Ansatz, und mit Disco steht für Interessierte ein in Erlang geschriebenes Framework zu Verfügung.
Rund um Hadoop haben sich weitere Projekte gegründet, die wie Hadoop ursprünglich freie Implementierungen von Google-Frameworks darstellen oder eigene Ansätze verfolgen. Zur ersten Gruppe gehören das Chukwa-Projekt, das das Monitoring von großen verteilten Systemen ermöglicht, sowie Hive, das auf Hadoop aufbauend eine Datawarehouse-Lösung mit eigener Abfragesprache anbietet. Zur Gruppe der sich an Google ausrichtenden Projekte gehört HBase, eine BigTable-Implementierung für große verteile Datenbanken. Das Pig-Projekt erlaubt, MapReduce-Anwendungen in einer eigenen Abfragesprache zu schreiben, die es in MapReduce-Jobs übersetzt, sodass der Anwender sich auf die Datenanalyse konzentrieren kann.
Unter dem Dach des Lucene-Projekts ist Mahout beheimatet, das auf Basis von Hadoop ein Framework für maschinelles Lernen bereitstellt. Viele der Projekte treiben maßgeblich Yahoo-Mitarbeiter als Open-Source-Projekte voran, da die Firma der größte Hadoop-Anwender ist. Zeitweise arbeitete auch Doug Cutting für Yahoo, bevor er zu Cloudera wechselte.
Fazit
MapReduce wird oft fälschlicherweise direkt mit relationalen Datenbanksystemen verglichen und als ineffizienter Weg zur Datenverwaltung dargestellt. Die Kritik übersieht, dass MapReduce kein Datenbanksystem ist, sondern ein Programmiermodell, mit dem sich auch Daten auswerten lassen, das aber nicht darauf beschränkt ist. Mit Hadoop und MapReduce können allgemein ausgedrückt Aufgaben gelöst werden, deren Eingabedaten sich gut in Teilstücke partitionieren lassen, um anschließend als unabhängige Teilprobleme bearbeitet werden zu können, was der Aufteilung der Daten in einzelne Blöcke im HDFS entspricht. Beispiele hierfür sind das Durchsuchen von Textdateien oder ihre Formatkonvertierung.
Nicht geeignet ist Hadoop für Aufgaben, die als Ganzes durch Abhängigkeiten während der Verarbeitung nicht partitionierbar sind, etwa die einer Wettersimulation. Neben seinem parallelen Ansatz eignet sich Hadoop durch seine flexible Architektur gut zur Verarbeitung von unstrukturierten Daten, für die es kein Schema gibt, deren Aufbereitung und Import in eine Datenbank oder in ein anderes System nicht im Verhältnis zur Häufigkeit ihres Zugriff stehen oder die nur im Rohformat vorliegen, da sie wiederholt, aber immer unter einer anderen Fragestellung verarbeitet werden. Das übersehen Kritiker oft, wenn sie MapReduce in einen direkten Vergleich mit relationalen Datenbanksystemen setzen. Wie so oft gilt, dass Aufgabe und Lösung zueinander passen müssen.
Erste Erfahrungen und die breite Anwendung von Hadoop haben den positiven Eindruck eines Frameworks hinterlassen, mit dem sich zuverlässig MapReduce-Anwendungen für den produktiven Einsatz entwickeln lassen. Wer nach einem alternativen Ansatz für die Massendatenverarbeitung sucht, sollte sich mit Hadoop näher beschäftigen.
Ein zweiter Artikel wird einen praktischen Einblick in die Entwicklung von MapReduce-Anwendungen geben.
Oliver Fischer
arbeitet in Berlin und ist über www.swe-blog.net zu erreichen. Für wertvolle Kritik und Ergänzungen zum Artikel bedankt er sich bei Isabel Drost.
(ane)