MapReduce
MapReduce jest modelem programowania oraz platformą przetwarzania danych opracowaną w celu rozwiązywania problemów z zakresu big data na dużą skalę przy użyciu klastrów komputerów. Model ten rozdziela zadanie na dwie podstawowe fazy – Map oraz Reduce – a pomiędzy nimi odbywa się etap sortowania i przemieszczania danych, zwany Shuffle. Dzięki takiej strukturze MapReduce umożliwia równoległe przetwarzanie ogromnych zbiorów danych przy zachowaniu prostoty programistycznej.
Historia
Pomysł na MapReduce został przedstawiony w 2004 roku w artykule „MapReduce: Simplified Data Processing on Large Clusters” autorstwa Jeffreya Dean i Sanjaya Ghemawata z firmy Google. Wdrożenie tego modelu w praktyce nastąpiło w 2006 roku, kiedy Apache Hadoop – otwarto‑źródłowa implementacja MapReduce – został udostępniony społeczności open‑source. Od tego czasu technologia stała się fundamentem wielu systemów analitycznych i przetwarzania danych, m.in. Amazon EMR, Cloudera i Hortonworks.
Podstawowe pojęcia
- Map – funkcja przetwarzająca każdy rekord wejściowy i generująca zestaw par (klucz, wartość). Działa niezależnie na poszczególnych fragmentach danych, co umożliwia wysoką paralelność.
- Shuffle – etap pośredni, w którym wszystkie pary (klucz, wartość) są grupowane według klucza. Dane są przenoszone pomiędzy węzłami klastra, aby wszystkie wartości o tym samym kluczu trafiły do tego samego procesu Reduce.
- Reduce – funkcja agregująca wszystkie wartości powiązane z danym kluczem i produkująca ostateczny wynik (np. sumę, średnią, listę).
Architektura i komponenty
Typowa implementacja MapReduce składa się z następujących elementów:
- JobTracker (lub ApplicationMaster w nowszych wersjach YARN) – zarządza planowaniem zadań, ich monitorowaniem oraz alokacją zasobów.
- TaskTracker (lub NodeManager) – wykonuje przydzielone zadania Map i Reduce na poszczególnych węzłach.
- HDFS (Hadoop Distributed File System) – rozproszony system plików, w którym przechowywane są zarówno dane wejściowe, jak i wyniki pośrednie oraz końcowe.
Zastosowania
MapReduce znajduje zastosowanie w wielu dziedzinach, m.in.:
- Analiza logów internetowych (np. liczenie unikalnych odwiedzających).
- Indeksowanie dokumentów i wyszukiwanie pełnotekstowe.
- Przetwarzanie danych genomowych w bioinformatyce.
- Modelowanie i uczenie maszynowe w trybie wsadowym.
- Agregacja danych telemetrycznych w systemach IoT.
Korzyści
- Skalowalność – model pozwala na zwiększanie mocy obliczeniowej przez prostą dodawanie kolejnych węzłów do klastra.
- Odporność na awarie – w przypadku awarii pojedynczego węzła zadania są automatycznie przenoszone i ponownie uruchamiane na innych maszynach.
- Abstrakcja programistyczna – programiści muszą definiować jedynie funkcje Map i Reduce, a system zajmuje się rozdzielaniem pracy i komunikacją.
Ograniczenia
- Model wymaga, aby przetwarzanie mogło być wyrażone jako dwie fazy, co nie zawsze jest optymalne dla algorytmów iteracyjnych.
- Wysoki narzut związany z etapem Shuffle i zapisem/odczytem z HDFS, co ogranicza wydajność w zadaniach wymagających częstej wymiany danych.
- MapReduce nie jest odpowiedni do pracy w czasie rzeczywistym – do tego służą systemy takie jak Apache Spark czy Apache Flink.
Powiązane technologie
W ekosystemie przetwarzania dużych zbiorów danych MapReduce współistnieje z wieloma innymi narzędziami i koncepcjami:
- Apache Spark – platforma oferująca podobny model programowania, ale z pamięcią operacyjną (in‑memory) i wsparciem dla przetwarzania strumieniowego.
- Apache Flink – system do przetwarzania danych w trybie ciągłym i wsadowym.
- YARN (Yet Another Resource Negotiator) – menedżer zasobów w Hadoopzie, umożliwiający uruchamianie różnych typów zadań, w tym MapReduce.
- Rozproszone systemy – kontekst, w którym MapReduce funkcjonuje, zapewniając spójność i dostępność danych.
Przykład implementacji
Poniżej przedstawiono prosty przykład programu MapReduce napisanego w języku Java, którego celem jest zliczanie wystąpień słów w zbiorze tekstowym.
public class WordCount {
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context)
throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class IntSumReducer
extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
W powyższym przykładzie TokenizerMapper emituje pary (słowo, 1), a IntSumReducer sumuje wszystkie wartości dla tego samego słowa, dając ostateczną liczbę wystąpień.
Patrzenie w przyszłość
Choć MapReduce w czystej formie traci na popularności na rzecz szybszych rozwiązań, nadal jest ważnym elementem zrozumienia przetwarzania rozproszonego i stanowi podstawę dla wielu współczesnych systemów analitycznych. Jego zasady wpływają na projektowanie algorytmów w Spark, Flink i innych platformach, które rozszerzają model o możliwości przetwarzania w pamięci, iteracyjne oraz strumieniowe.
Artykuł opracowany na podstawie dostępnych źródeł publicznych oraz dokumentacji projektów Apache Hadoop, Google i Apache Spark.