Kapitel 1. Einführung in die Stream-Verarbeitung

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Im Jahr 2011 sagte Marc Andreessen bekanntermaßen, dass "Software die Welt auffrisst", und bezog sich damit auf die boomende digitale Wirtschaft, zu einer Zeit, als viele Unternehmen mit den Herausforderungen der digitalen Transformation konfrontiert waren. Erfolgreiche Online-Unternehmen, die "online" und "mobil" arbeiten, verdrängten ihre traditionellen "brick-and-mortar"-Pendants.

Stell dir zum Beispiel den traditionellen Kauf einer neuen Kamera in einem Fotogeschäft vor: Wir besuchen das Geschäft, sehen uns um, stellen dem Verkäufer vielleicht ein paar Fragen, entscheiden uns und kaufen schließlich ein Modell, das unseren Wünschen und Erwartungen entspricht. Nach dem Kauf hat das Geschäft eine Kreditkartentransaktion registriert - oder nur eine Änderung des Bargelds im Falle einer Barzahlung - und der Geschäftsführer weiß, dass er ein bestimmtes Kameramodell weniger auf Lager hat.

Nehmen wir diese Erfahrung nun online: Zuerst beginnen wir mit der Suche im Internet. Wir besuchen mehrere Online-Shops und hinterlassen dabei digitale Spuren. Auf Websites werden plötzlich Werbeanzeigen für die Kamera, die wir uns angesehen haben, sowie für konkurrierende Alternativen angezeigt. Schließlich finden wir einen Online-Shop, der uns das beste Angebot macht, und kaufen die Kamera. Wir erstellen ein Konto. Unsere persönlichen Daten werden registriert und mit dem Kauf verknüpft. Während wir unseren Kauf abschließen, werden uns zusätzliche Optionen angeboten, die angeblich bei anderen Personen, die dieselbe Kamera gekauft haben, beliebt sind. Jede unserer digitalen Interaktionen, wie die Suche nach Schlüsselwörtern im Internet, das Klicken auf einen Link oder das Lesen einer bestimmten Seite, erzeugt eine Reihe von Ereignissen, die gesammelt und in einen Geschäftswert umgewandelt werden, wie personalisierte Werbung oder Kaufempfehlungen.

In Anlehnung an Andreessens Zitat sagte Dries Buytaert 2015: "Nein, eigentlich fressen die Daten die Welt auf." Damit meinte er, dass die disruptiven Unternehmen von heute nicht mehr wegen ihrer Software disruptiv sind, sondern wegen der einzigartigen Daten, die sie sammeln, und wegen ihrer Fähigkeit, diese Daten in Wert zu verwandeln.

Die Einführung von Stream-Processing-Technologien wird durch das wachsende Bedürfnis von Unternehmen vorangetrieben, die Zeit zu verkürzen, die sie benötigen, um auf Veränderungen in ihrem betrieblichen Umfeld zu reagieren und sich anzupassen. Diese Art der Verarbeitung von Daten, während sie eingehen, bietet einen technischen und strategischen Vorteil. Beispiele für diese fortlaufende Einführung sind Sektoren wie der Internethandel, in dem kontinuierlich Datenpipelines von Unternehmen erstellt werden, die rund um die Uhr mit Kunden interagieren, oder Kreditkartenunternehmen, die Transaktionen analysieren, während sie stattfinden, um betrügerische Aktivitäten zu erkennen und zu stoppen, während sie stattfinden.

Ein weiterer Grund für die Verarbeitung von Datenströmen ist die Tatsache, dass unsere Fähigkeit, Daten zu erzeugen, unsere Fähigkeit, sie sinnvoll zu nutzen, bei weitem übersteigt. Die Zahl der rechenfähigen Geräte in unserem privaten und beruflichen Umfeld nimmt ständig zu: Fernseher, vernetzte Autos, Smartphones, Fahrradcomputer, Smartwatches, Überwachungskameras, Thermostate und so weiter. Wir umgeben uns mit Geräten, die Ereignisprotokolle erstellen: Ströme von Nachrichten, die die Aktionen und Vorfälle darstellen, die Teil der Geschichte des Geräts in seinem Kontext sind. Indem wir diese Geräte immer mehr miteinander vernetzen, schaffen wir die Möglichkeit, auf diese Ereignisprotokolle zuzugreifen und sie zu analysieren. Dieses Phänomen öffnet die Tür zu einem unglaublichen Schub an Kreativität und Innovation im Bereich der echtzeitnahen Datenanalyse, vorausgesetzt, wir finden einen Weg, diese Analyse nachvollziehbar zu machen. In dieser Welt der aggregierten Ereignisprotokolle bietet die Stream-Verarbeitung die ressourcenschonendste Möglichkeit, die Analyse von Datenströmen zu erleichtern.

Es ist keine Überraschung, dass nicht nur die Daten die Welt fressen, sondern auch das Streaming von Daten.

In diesem Kapitel beginnen wir unsere Reise in die Stream-Verarbeitung mit Apache Spark. Um die Fähigkeiten von Spark im Bereich der Stream-Verarbeitung zu erörtern, müssen wir ein gemeinsames Verständnis dafür entwickeln, was Stream-Verarbeitung ist, welche Anwendungen es gibt und welche Herausforderungen sie mit sich bringt. Nachdem wir diese gemeinsame Sprache entwickelt haben, stellen wir Apache Spark als generisches Datenverarbeitungs-Framework vor, das die Anforderungen von Batch- und Streaming-Workloads mit einem einheitlichen Modell bewältigen kann. Zum Schluss gehen wir auf die Streaming-Fähigkeiten von Spark ein, indem wir die beiden verfügbaren APIs vorstellen: Spark Streaming und Structured Streaming. Wir gehen kurz auf ihre wichtigsten Merkmale ein, um dir einen kleinen Einblick in den Rest des Buches zu geben.

Was ist Stream Processing?

Stream Processing ist die Disziplin und die damit verbundenen Techniken, die verwendet werden, um Informationen aus unbegrenzten Daten zu extrahieren.

In seinem Buch Streaming Systems definiert Tyler Akidau unbegrenzte Daten wie folgt:

Eine Art von Datensatz, der unendlich groß ist (zumindest theoretisch).

Da unsere Informationssysteme auf Hardware mit endlichen Ressourcen wie Arbeitsspeicher und Speicherkapazität aufgebaut sind, können sie unmöglich unbegrenzte Datensätze speichern. Stattdessen beobachten wir die Daten, wie sie in Form eines Stroms von Ereignissen im Laufe der Zeit im Verarbeitungssystem ankommen. Wir nennen einen Datenstrom.

In hingegen betrachten wir begrenzte Daten als einen Datensatz mit bekannter Größe. Wir können die Anzahl der Elemente in einem begrenzten Datensatz zählen.

Batch- versus Stream-Verarbeitung

Wie verarbeiten wir beide Arten von Datensätzen? Bei der Stapelverarbeitung geht es um die rechnerische Analyse von begrenzten Datensätzen. In der Praxis bedeutet das, dass diese Datensätze in ihrer Gesamtheit auf irgendeiner Art von Speicherung verfügbar und abrufbar sind. Wir kennen die Größe des Datensatzes zu Beginn des Rechenvorgangs und die Dauer dieses Vorgangs ist zeitlich begrenzt.

Im Gegensatz dazu geht es bei der Stream-Verarbeitung um die Verarbeitung der Daten, sobald sie im System ankommen. Da Datenströme unbegrenzt sind, müssen die Stream-Prozessoren so lange laufen, wie der Stream neue Daten liefert. Das kann, wie wir gelernt haben, theoretisch ewig sein.

Stream-Processing-Systeme wenden Programmier- und Betriebstechniken an, um die Verarbeitung von potenziell unendlichen Datenströmen mit einer begrenzten Menge an Rechenressourcen zu ermöglichen.

Der Begriff der Zeit in der Stromverarbeitung

Daten können in zwei Formen vorkommen:

  • In Ruhe, in Form einer Datei, des Inhalts einer Datenbank oder einer anderen Art von Aufzeichnung

  • In Bewegung, als kontinuierlich erzeugte Signalfolgen, wie die Messung eines Sensors oder GPS-Signale von fahrenden Fahrzeugen

Wir haben bereits besprochen, dass ein Stream-Processing-Programm ein Programm ist, das davon ausgeht, dass seine Eingabe potenziell unendlich groß ist. Genauer gesagt, geht ein Stream-Processing-Programm davon aus, dass seine Eingabe eine Folge von Signalen unbestimmter Länge ist, die im Laufe der Zeit beobachtet werden.

Aus der Sicht von sind ruhende Daten Daten aus der Vergangenheit: Alle begrenzten Datensätze, egal ob sie in Dateien oder Datenbanken gespeichert sind, waren ursprünglich ein Strom von Daten, die im Laufe der Zeit in einer Speicherung gesammelt wurden. Die Datenbank des Benutzers, alle Bestellungen des letzten Quartals, die GPS-Koordinaten von Taxifahrten in einer Stadt usw. begannen alle als einzelne Ereignisse, die in einem Speicher gesammelt wurden.

Der Versuch, auf Daten in Bewegung zu schließen, ist eine größere Herausforderung. Zwischen dem Zeitpunkt, an dem die Daten ursprünglich erzeugt werden, und dem Zeitpunkt, an dem sie für die Verarbeitung zur Verfügung stehen, besteht ein Zeitunterschied. Dieses Zeitdelta kann sehr kurz sein, wie z. B. Web-Log-Ereignisse, die innerhalb desselben Rechenzentrums erzeugt und verarbeitet werden, oder sehr viel länger, wie z. B. die GPS-Daten eines Autos, das durch einen Tunnel fährt und erst dann versendet wird, wenn das Fahrzeug nach dem Verlassen des Tunnels seine drahtlose Verbindung wiederherstellt.

Wir können feststellen, dass es eine Zeitlinie gibt, wann die Ereignisse produziert wurden, und eine andere, wann die Ereignisse vom Stream-Processing-System bearbeitet werden. Diese Zeitlinien sind so bedeutsam, dass wir ihnen spezielle Namen geben:

Zeit der Veranstaltung

Die Zeit, zu der das Ereignis erstellt wurde. Die Zeitinformation wird von der lokalen Uhr des Geräts geliefert, das das Ereignis erzeugt.

Bearbeitungszeit

Die Zeit, zu der das Ereignis vom Stream-Processing-System bearbeitet wird. Das ist die Uhr des Servers, auf dem die Verarbeitungslogik läuft. Sie ist in der Regel aus technischen Gründen relevant, z. B. für die Berechnung der Verzögerung bei der Verarbeitung oder als Kriterium für die Ermittlung doppelter Ausgaben.

Die Unterscheidung zwischen diesen Zeitlinien wird sehr wichtig, wenn wir die Ereignisse zueinander in Beziehung setzen, ordnen oder zusammenfassen müssen.

Der Faktor Ungewissheit

In einer Zeitleiste beziehen sich ruhende Daten auf die Vergangenheit und Daten in Bewegung können als Gegenwart betrachtet werden. Aber was ist mit der Zukunft? Einer der subtilsten Aspekte dieser Diskussion ist, dass sie keine Annahmen über den Durchsatz macht, mit dem das System Ereignisse empfängt.

Bei Streaming-Systemen ist es im Allgemeinen nicht erforderlich, dass die Eingaben in regelmäßigen Abständen, auf einmal oder in einem bestimmten Rhythmus erzeugt werden. Da Berechnungen in der Regel mit Kosten verbunden sind, ist es eine Herausforderung, Lastspitzen vorherzusagen: das plötzliche Eintreffen von Eingabeelementen mit den für ihre Verarbeitung erforderlichen Rechenressourcen abzugleichen.

Wenn wir über die nötige Rechenkapazität verfügen, um einen plötzlichen Zustrom von Eingabedaten zu bewältigen, wird unser System die erwarteten Ergebnisse liefern, aber wenn wir nicht für einen solchen Ansturm von Eingabedaten geplant haben, kann es bei einigen Streaming-Systemen zu Verzögerungen, Ressourcenengpässen oder Ausfällen kommen.

Der Umgang mit Ungewissheit ist ein wichtiger Aspekt der Stromverarbeitung.

Zusammenfassend lässt sich sagen, dass die Datenstromverarbeitung es uns ermöglicht, Informationen aus unendlichen Datenströmen zu extrahieren, die als Ereignisse im Laufe der Zeit beobachtet werden. Wenn wir jedoch Daten empfangen und verarbeiten, müssen wir mit der zusätzlichen Komplexität der Ereigniszeit und der Ungewissheit umgehen, die durch einen unbegrenzten Input entsteht.

Warum sollten wir uns mit dem zusätzlichen Aufwand befassen? Im nächsten Abschnitt werfen wir einen Blick auf eine Reihe von Anwendungsfällen, die den Mehrwert der Datenstromverarbeitung verdeutlichen und zeigen, wie sie das Versprechen einlöst, schnellere, verwertbare Erkenntnisse und damit einen geschäftlichen Nutzen aus Datenströmen zu ziehen.

Einige Beispiele für Stream Processing

Der Einsatz von Stream-Processing ist so vielfältig wie unsere Fähigkeit, uns neue innovative Echtzeitanwendungen für Daten vorzustellen. Die folgenden Anwendungsfälle, an denen die Autoren auf die eine oder andere Weise beteiligt waren, sind nur eine kleine Auswahl, mit der wir das breite Anwendungsspektrum von Stream-Processing illustrieren wollen:

Geräteüberwachung

Ein kleines Startup-Unternehmen ( ) hat einen cloudbasierten Internet of Things (IoT)-Geräte-Monitor eingeführt, der Daten von bis zu 10 Millionen Geräten sammeln, verarbeiten und speichern kann. Mehrere Stream-Prozessoren wurden eingesetzt, um verschiedene Teile der Anwendung zu betreiben, von Echtzeit-Dashboard-Updates mit In-Memory-Speichern bis hin zu kontinuierlichen Datenaggregaten, wie z. B. eindeutige Zählungen und Minimum/Maximum-Messungen.

Fehlersuche

Ein großer Hardwarehersteller ( ) verwendet eine komplexe Stream-Processing-Pipeline, um Gerätemetriken zu empfangen. Mithilfe von Zeitreihenanalysen werden potenzielle Fehler erkannt und Korrekturmaßnahmen automatisch an das Gerät zurückgesendet.

Modernisierung der Rechnungsstellung

Eine gut etablierte Versicherungsgesellschaft ( ) hat ihr Abrechnungssystem auf eine Streaming-Pipeline umgestellt. Batch-Exporte aus der bestehenden Mainframe-Infrastruktur werden durch dieses System gestreamt, um die bestehenden Abrechnungsprozesse zu erfüllen und gleichzeitig neue Echtzeitströme von Versicherungsagenten mit derselben Logik bedienen zu können.

Flottenmanagement

Ein Fuhrparkmanagementunternehmen ( ) hat Geräte installiert, die Echtzeitdaten von den verwalteten Fahrzeugen melden können, z. B. Standort, Motorparameter und Kraftstoffvorrat, so dass es Regeln wie geografische Beschränkungen durchsetzen und das Fahrerverhalten in Bezug auf Geschwindigkeitsbegrenzungen analysieren kann.

Medienempfehlungen

Ein nationales Medienunternehmen ( ) hat eine Streaming-Pipeline eingerichtet, um neue Videos, wie z. B. Nachrichtenberichte, in sein Empfehlungssystem aufzunehmen und die Videos für die personalisierten Vorschläge seiner Nutzer/innen verfügbar zu machen, sobald sie in den Medienspeicher des Unternehmens aufgenommen werden. Das vorherige System des Unternehmens brauchte Stunden, um dies zu tun.

Schnellere Kredite

Eine Bank, die im Kreditgeschäft tätig ist, konnte die Kreditgenehmigung von Stunden auf Sekunden verkürzen, indem sie mehrere Datenströme in einer Streaming-Anwendung zusammenführte.

Ein gemeinsamer Nenner dieser Anwendungsfälle ist die Notwendigkeit für das Unternehmen, die Daten zu verarbeiten und innerhalb einer kurzen Zeitspanne nach Erhalt der Daten verwertbare Erkenntnisse zu gewinnen. Diese Zeitspanne ist relativ zum Anwendungsfall: Obwohl Minuten eine sehr schnelle Bearbeitungszeit für eine Kreditgenehmigung sind, sind wahrscheinlich Millisekunden notwendig, um einen Geräteausfall zu erkennen und eine Korrekturmaßnahme innerhalb eines bestimmten Servicelevels einzuleiten.

In allen Fällen können wir argumentieren, dass Daten besser sind, wenn sie so frisch wie möglich konsumiert werden.

Jetzt, wo wir wissen, was Stream Processing ist und wie es heute eingesetzt wird, ist es an der Zeit, sich mit den Konzepten zu befassen, die seiner Umsetzung zugrunde liegen.

Skalierung der Datenverarbeitung

Bevor wir die Auswirkungen verteilter Berechnungen bei der Stream-Verarbeitung erörtern, werfen wir einen kurzen Blick auf MapReduce, ein Berechnungsmodell, das den Grundstein für eine skalierbare und zuverlässige Datenverarbeitung gelegt hat.

MapReduce

Die Geschichte der Programmierung für verteilte Systeme erlebte im Februar 2003 ein bemerkenswertes Ereignis. Nachdem Jeff Dean und Sanjay Gemawhat die Crawling- und Indexierungssysteme von Google in mehreren Iterationen umgeschrieben hatten, bemerkten sie einige Operationen, die sie über eine gemeinsame Schnittstelle zugänglich machen konnten. Daraufhin entwickelten sie MapReduce, ein System zur verteilten Verarbeitung auf großen Clustern bei Google.

Einer der Gründe, warum wir MapReduce nicht schon früher entwickelt haben, war wahrscheinlich, dass wir in kleineren Maßstäben arbeiteten, als unsere Berechnungen weniger Maschinen benötigten und daher Robustheit keine so große Rolle spielte: Es war in Ordnung, einige Berechnungen regelmäßig zu überprüfen und die gesamte Berechnung von einem Prüfpunkt aus neu zu starten, wenn eine Maschine ausfiel. Sobald man jedoch einen bestimmten Maßstab erreicht, wird das ziemlich unhaltbar, da man immer wieder neu starten müsste und nie vorankäme.

Jeff Dean, E-Mail an Bradford F. Lyon, August 2013

MapReduce ist in erster Linie eine Programmier-API und in zweiter Linie ein Satz von Komponenten, die die Programmierung für ein verteiltes System relativ einfacher machen als alle seine Vorgänger.

Seine Kernpunkte sind zwei Funktionen:

Karte

Die Map-Operation erhält als Argument eine Funktion, die auf jedes Element der Sammlung angewendet werden soll. Die Elemente der Sammlung werden verteilt über das verteilte Dateisystem gelesen, ein Chunk pro Executor-Maschine. Dann wird auf alle Elemente der Sammlung, die sich in dem lokalen Chunk befinden, die Funktion angewendet und der Executor gibt das Ergebnis dieser Anwendung aus, falls vorhanden.

Reduziere

Die Operation reduce nimmt zwei Argumente entgegen: Das eine ist ein neutrales Element, das die Operation reduce zurückgeben würde, wenn sie eine leere Sammlung übergeben würde. Das andere ist eine Aggregationsoperation, die den aktuellen Wert eines Aggregats und ein neues Element der Sammlung nimmt und zu einem neuen Aggregat zusammenfasst.

Kombinationen dieser beiden Funktionen höherer Ordnung sind mächtig genug, um jede Operation auszudrücken, die wir mit einem Datensatz durchführen wollen.

Die Lektion ist gelernt: Skalierbarkeit und Fehlertoleranz

Aus der Sicht des Programmierers sind hier die wichtigsten Vorteile von MapReduce:

  • Es hat eine einfache API.

  • Sie bietet eine sehr hohe Ausdruckskraft.

  • Die Schwierigkeit, ein Programm zu verteilen, wird von den Schultern der Programmierer/innen auf die Schultern der Bibliotheksentwickler/innen verlagert. Vor allem ist die Ausfallsicherheit in das Modell eingebaut.

Obwohl diese Eigenschaften das Modell attraktiv machen, liegt der Haupterfolg von MapReduce in seiner Fähigkeit, nachhaltig zu wachsen. Wenn die Datenmengen steigen und die wachsenden Geschäftsanforderungen zu mehr Aufträgen zur Informationsextraktion führen, weist das MapReduce-Modell zwei entscheidende Eigenschaften auf:

Skalierbarkeit

Wenn die Datensätze wachsen, ist es möglich, mehr Ressourcen zum Cluster hinzuzufügen, um eine stabile Verarbeitungsleistung zu erhalten.

Fehlertoleranz

Das System kann Teilausfälle überstehen und sich davon erholen. Alle Daten werden repliziert. Wenn ein datenführender Executor ausfällt, reicht es aus, die Aufgabe, die auf dem abgestürzten Executor lief, neu zu starten. Da der Master diese Aufgabe im Auge behält, stellt dies außer der Neuplanung kein besonderes Problem dar.

Diese beiden Eigenschaften zusammen ergeben ein System, das in der Lage ist, Arbeitslasten in einer grundsätzlich unzuverlässigen Umgebung konstant aufrechtzuerhalten - Eigenschaften, die wir auch für die Streamverarbeitung benötigen.

Verteilte Stream-Verarbeitung

Ein grundlegender Unterschied der Stream-Verarbeitung zum MapReduce-Modell und zur Batch-Verarbeitung im Allgemeinen besteht darin, dass wir bei der Batch-Verarbeitung zwar Zugriff auf den gesamten Datensatz haben, bei Streams aber immer nur einen kleinen Teil des Datensatzes sehen.

Diese Situation wird in einem verteilten System noch verschärft. Um die Verarbeitungslast auf mehrere Executors zu verteilen, teilen wir den Eingabestrom weiter in Partitionen auf. Jeder Executor bekommt nur einen Teil des gesamten Stroms zu sehen.

Die Herausforderung für ein verteiltes Stream-Processing-Framework besteht darin, eine Abstraktion zu schaffen, die diese Komplexität vor dem Benutzer verbirgt und es uns ermöglicht, den Stream als Ganzes zu betrachten.

Stateful Stream Processing in einem verteilten System

Stellen wir uns vor, dass wir bei einer Präsidentschaftswahl die Stimmen auszählen. Der klassische Batch-Ansatz wäre, zu warten, bis alle Stimmen abgegeben wurden, und dann mit der Auszählung zu beginnen. Auch wenn dieser Ansatz zu einem korrekten Endergebnis führt, würde er im Laufe des Tages für sehr langweilige Nachrichten sorgen, weil bis zum Ende des Wahlvorgangs keine (Zwischen-)Ergebnisse bekannt sind.

Ein spannenderes Szenario ist, wenn wir die Stimmen pro Kandidat/in zählen können, während jede Stimme abgegeben wird. Zu jedem Zeitpunkt haben wir eine Teilauszählung pro Teilnehmer/in, die uns den aktuellen Stand sowie den Trend der Stimmabgabe zeigt. Wir können wahrscheinlich ein Ergebnis vorhersagen.

Um dieses Szenario zu verwirklichen, muss der Stream-Prozessor ein internes Register der bisher gesehenen Stimmen führen. Um eine konsistente Auszählung zu gewährleisten, muss sich dieses Register von jedem Teilausfall erholen. Wir können die Bürgerinnen und Bürger nämlich nicht bitten, ihre Stimme aufgrund eines technischen Fehlers erneut abzugeben.

Außerdem kann eine eventuelle Fehlerbehebung das Endergebnis nicht beeinflussen. Wir können nicht riskieren, als Nebeneffekt eines schlecht wiederhergestellten Systems den falschen Kandidaten zum Sieger zu erklären.

Dieses Szenario veranschaulicht die Herausforderungen der zustandsbehafteten Stream-Verarbeitung in einer verteilten Umgebung. Die zustandsbehaftete Verarbeitung stellt eine zusätzliche Belastung für das System dar:

  • Wir müssen sicherstellen, dass der Zustand über die Zeit erhalten bleibt.

  • Wir brauchen Garantien für die Datenkonsistenz, auch bei teilweisen Systemausfällen.

Wie du im Laufe dieses Buches sehen wirst, ist die Bewältigung dieser Probleme ein wichtiger Aspekt der Streamverarbeitung.

Nachdem wir nun die Gründe für die Beliebtheit der Stream-Verarbeitung und die Herausforderungen dieser Disziplin besser kennen, können wir Apache Spark vorstellen. Als einheitliche Datenanalyse-Engine bietet Spark Datenverarbeitungsfunktionen sowohl für Batch als auch für Streaming und ist damit eine ausgezeichnete Wahl, um die Anforderungen datenintensiver Anwendungen zu erfüllen, wie wir im Folgenden sehen werden.

Einführung in Apache Spark

Apache Spark ist ein schnelles, zuverlässiges und fehlertolerantes verteiltes Computing-Framework für die Verarbeitung großer Datenmengen.

Die erste Welle: Funktionale APIs

In den Anfangstagen wurde der Durchbruch von Spark durch die neuartige Nutzung des Speichers und die ausdrucksstarke funktionale API vorangetrieben. Das Speichermodell von nutzt den RAM-Speicher, um Daten während der Verarbeitung zwischenzuspeichern, was zu einer bis zu 100-mal schnelleren Verarbeitung führt als Hadoop MapReduce, die Open-Source-Implementierung von Googles MapReduce für Batch-Workloads.

Seine Kernabstraktion, das Resilient Distributed Dataset (RDD), brachte ein reichhaltiges funktionales Programmiermodell mit sich, das die Komplexität des verteilten Rechnens auf einem Cluster abstrahierte. führte die Konzepte der Transformationen und Aktionen ein, die ein ausdrucksstärkeres Programmiermodell boten als die Map- und Reduktionsstufen, die wir in der MapReduce-Übersicht besprochen haben. In diesem Modell drücken viele verfügbare Transformationen wie map, flatmap, join und filter die faule Umwandlung der Daten von einer internen Repräsentation in eine andere aus, während eifrige Operationen, die Actions genannt werden, die Berechnungen im verteilten System durchführen, um ein Ergebnis zu erzielen.

Die zweite Welle: SQL

Die zweite große Veränderung in der Geschichte des Spark-Projekts war die Einführung von Spark SQL und DataFrames (und später Dataset, einem stark typisierten DataFrame). Aus einer übergeordneten Perspektive betrachtet, fügt Spark SQL SQL-Unterstützung zu jedem Datensatz hinzu, der ein Schema hat. Es ermöglicht die Abfrage von kommagetrennten Werten (CSV), Parquet oder JSON-Datensätzen auf die gleiche Weise wie die Abfrage einer SQL-Datenbank.

Diese Entwicklung senkte auch die Hemmschwelle für die Nutzer. Fortgeschrittene verteilte Datenanalysen waren nicht mehr nur Softwareentwicklern vorbehalten, sondern auch Datenwissenschaftlern, Geschäftsanalysten und anderen Fachleuten, die mit SQL vertraut sind. Aus Sicht der Leistung brachte SparkSQL einen Abfrageoptimierer und eine physische Ausführungsengine in Spark ein, wodurch Spark noch schneller wurde und weniger Ressourcen verbrauchte.

Ein einheitlicher Motor

Heutzutage ist Spark eine einheitliche Analyse-Engine mit Batch- und Streaming-Funktionen, die mit einem polyglotten Ansatz zur Datenanalyse kompatibel ist und APIs in Scala, Java, Python und der Sprache R bietet.

Auch wenn wir uns im Rahmen dieses Buches auf die Streaming-Funktionen von Apache Spark konzentrieren, sind die Batch-Funktionen ebenso fortschrittlich und ergänzen Streaming-Anwendungen in hohem Maße. Das einheitliche Programmiermodell von Spark bedeutet, dass Entwickler nur ein neues Paradigma erlernen müssen, um sowohl Batch- als auch Streaming-Arbeitslasten zu bewältigen.

Hinweis

Im Verlauf des Buches verwenden wir Apache Spark und Spark synonym. Wir verwenden Apache Spark, wenn wir das Projekt oder den Open-Source-Aspekt hervorheben wollen, während wir Spark verwenden, um uns auf die Technologie im Allgemeinen zu beziehen.

Spark Komponenten

Abbildung 1-1 zeigt, wie Spark aus einer Kern-Engine, einer Reihe von darauf aufbauenden Abstraktionen (dargestellt als horizontale Schichten) und Bibliotheken besteht, die diese Abstraktionen nutzen, um einen bestimmten Bereich zu adressieren (vertikale Kästen). Wir haben die Bereiche, die in diesem Buch behandelt werden, hervorgehoben und diejenigen, die nicht behandelt werden, ausgegraut. Um mehr über die anderen Bereiche von Apache Spark zu erfahren, empfehlen wir Spark, The Definitive Guide von Bill Chambers und Matei Zaharia (O'Reilly) und High Performance Spark von Holden Karau und Rachel Warren (O'Reilly).

spas 0101
Abbildung 1-1. Abstraktionsebenen (horizontal) und Bibliotheken (vertikal) von Spark

Als Abstraktionsschichten in Spark haben wir folgende:

Spark-Kern

Enthält die Spark-Kernausführungs-Engine und eine Reihe von Low-Level-APIs zur Verteilung von Berechnungen auf einen Cluster von Rechenressourcen, die im Spark-Jargon Executors genannt werden. Seine Cluster-Abstraktion ermöglicht es, Arbeitslasten an YARN, Mesos und Kubernetes zu übermitteln sowie seinen eigenen Standalone-Cluster-Modus zu nutzen, in dem Spark als dedizierter Dienst in einem Cluster von Maschinen läuft. Seine Datenquellen-Abstraktion ermöglicht die Integration vieler verschiedener Datenanbieter wie Dateien, Blockstores, Datenbanken und Event-Broker.

Spark SQL

Implementiert die Higher-Level-APIs von Dataset und DataFrame von Spark und fügt SQL-Unterstützung für beliebige Datenquellen hinzu. Außerdem führt es eine Reihe von Leistungsverbesserungen durch die Catalyst-Abfrage-Engine und die Codegenerierung und Speicherverwaltung des Projekts Tungsten ein.

Die Bibliotheken, die auf diesen Abstraktionen aufbauen, adressieren verschiedene Bereiche der groß angelegten Datenanalytik: MLLib für maschinelles Lernen, GraphFrames für die Graphenanalyse und die beiden APIs für die Stream-Verarbeitung, die im Mittelpunkt dieses Buches stehen: Spark Streaming und Structured Streaming.

Spark Streaming

Spark Streaming war das erste Stream-Processing-Framework, das auf den verteilten Verarbeitungsfunktionen der Spark-Kern-Engine aufbaute. Es wurde in der Spark-Version 0.7.0 im Februar 2013 als Alphaversion eingeführt und hat sich im Laufe der Zeit zu einer ausgereiften API entwickelt, die in der Branche weit verbreitet ist, um große Datenströme zu verarbeiten.

Das Konzept von Spark Streaming basiert auf einer einfachen, aber leistungsstarken Prämisse: Die verteilten Rechenkapazitäten von Spark werden auf die Stream-Verarbeitung angewendet, indem kontinuierliche Datenströme in diskrete Datensammlungen umgewandelt werden, mit denen Spark arbeiten kann. Dieser Ansatz der Stream-Verarbeitung wird als Microbatch-Modell bezeichnet; dies steht im Gegensatz zum Element-at-time-Modell, das in den meisten anderen Stream-Verarbeitungsimplementierungen vorherrscht.

Spark Streaming verwendet dasselbe funktionale Programmierparadigma wie der Spark-Kern, führt aber eine neue Abstraktion ein, den diskretisierten Stream oder DStream, der ein Programmiermodell für die Bearbeitung der zugrunde liegenden Daten im Stream bereitstellt.

Strukturiertes Streaming

Structured Streaming ist ein Stream-Prozessor, der auf der Abstraktion von Spark SQL aufbaut. Er erweitert die APIs Dataset und DataFrame um Streaming-Fähigkeiten. Als solcher übernimmt er das schemaorientierte Transformationsmodell - daher der strukturierte Teil seines Namens - und erbt alle in Spark SQL implementierten Optimierungen.

Structured Streaming wurde im Juli 2016 mit Spark 2.0 als experimentelle API eingeführt. Ein Jahr später erreichte es mit Spark 2.2 die allgemeine Verfügbarkeit und wurde für den produktiven Einsatz freigegeben. Als relativ neue Entwicklung wird Structured Streaming mit jeder neuen Version von Spark weiter schnell weiterentwickelt.

Structured Streaming verwendet ein deklaratives Modell, um Daten aus einem Stream oder einer Reihe von Streams zu erfassen. Um die API in vollem Umfang nutzen zu können, ist die Angabe eines Schemas für die Daten im Stream erforderlich. Neben der Unterstützung des allgemeinen Transformationsmodells, das von den APIs Dataset und DataFrame bereitgestellt wird, werden Stream-spezifische Funktionen wie die Unterstützung von Event-Time, Streaming Joins und die Trennung von der zugrunde liegenden Laufzeit eingeführt. Letzteres öffnet die Tür für die Implementierung von Laufzeiten mit unterschiedlichen Ausführungsmodellen. Die Standardimplementierung verwendet den klassischen Microbatch-Ansatz, während ein neueres Continuous-Processing-Backend experimentelle Unterstützung für einen kontinuierlichen Ausführungsmodus nahe Echtzeit bietet.

Structured Streaming bietet ein einheitliches Modell, das die Stream-Verarbeitung auf die gleiche Ebene wie stapelorientierte Anwendungen bringt und einen großen Teil der kognitiven Belastung bei der Argumentation über Stream-Verarbeitung beseitigt.

Wie geht es weiter?

Wenn du den Drang verspürst, eine dieser beiden APIs sofort zu lernen, kannst du direkt zu Structured Streaming in Teil II oder Spark Streaming in Teil III springen.

Wenn du noch nicht mit Stream Processing vertraut bist, empfehlen wir dir, diesen ersten Teil des Buches zu lesen, da wir hier das Vokabular und die allgemeinen Konzepte aufbauen, die wir bei der Diskussion der einzelnen Frameworks verwenden.

Get Stream Processing mit Apache Spark now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.