Management Summary
In diesem Beitrag sollen fünf Technologien vorgestellt werden, die jeder Data Engineer für seine tägliche Arbeit kennen und beherrschen sollte. Aufgeführt werden Spark als Data Processing Tool im Big Data Umfeld, Kafka als Streaming Platform, Airflow und Serverless-Architektur zur Koordinierung bzw. Orchestrierung. Zuvor werden Stellenwert und Rolle von SQL (Structured Query Language) und relationalen Datenbanken besprochen.
Trotz des stetigen Wandels hat sich SQL eine Sonderposition herausgearbeitet und findet sich auch in neuen Entwicklungen als Schnittstelle wieder. Mitnichten ist es aber so, dass wie vor Jahrzehnten ein stabiles Wissen von dieser Query-Sprache ausreicht, um das Gros der Datenarbeiten bewältigen zu können. Dafür ist zum einen die Datenlandschaft, zum anderen die verarbeiteten Daten mittlerweile zu heterogen geworden. Darüber hinaus reicht es in vielen Fällen nicht mehr aus, Daten nach einem zeitlich fixierten Schema zu aktualisieren oder die zu prozessierenden Datenmengen sind schlicht nicht mehr von klassischen ETL (Extract-Transform-Load) Prozessen in Datenbanken zu schultern.
Data Streaming Plattformen wie Apache Kafka sind eine Antwort auf die Real-time-Problematik, dabei hat es die Möglichkeit mit den Anforderungen zu skalieren und für Ausfallsicherheit zu sorgen. Bezüglich der Möglichkeit des Prozessierens enorm großer Datenmengen ist Spark das Tool der Wahl. Ebenso wie Kafka kann es mit den Anforderungen skalieren. Ferner bietet es eine großzügige Auswahl an Implementierungssprachen an.
Auf die Heterogenität der eingesetzten Data Stores und Prozessierungsframeworks kann man mit zwei Ansätzen antworten: eine zentrale (globale) Orchestrierung, die die verschiedenen Komponenten bedienen kann, wie es Airflow auf beeindruckende Weise tut; eine dezentrale (lokale) Lösung, die nur auf spezifische Signale einzelner Komponenten reagiert, wie es der Serverless-Ansatz vorsieht. Alle gängigen Cloud-Anbieter können einen solchen Service vorweisen. Zusammenfassend findet man hier also eine Mischung an Tools als Antwort auf die aktuellen Fragen im Data Engineering.
Intro
Die Datenlandschaft war in den letzten Jahrzehnten geprägt von einer zunehmenden Dynamik. Galten bis zum Jahrtausendwechsel Data Warehouses und relationale Datenbank-Managementsysteme (RDBMS) noch als der Goldstandard für die Datenhaltung und -aufbereitung, so durchbrach vor allem die Verbreitung und die Dynamik des Internets diese Alleinstellung. Zum einen vervielfachte sich damit die Datenmenge, zum anderen galt nun das Interesse vermehrt auch semi-strukturierten und unstrukturierteren Daten. Man war also im Big Data Zeitalter angekommen. Der nächste Schub wurde durch die von mobilen Endgeräten und Sensoren generierten Datenflüsse (Stichwort: Internet of Things) verursacht. Es ging nun nicht mehr nur darum, den erneut enorm gestiegenen Datenaufwand zu bewältigen, sondern Ereignisse aus vielen Datenpunkten in Echtzeit zu erkennen und darauf reagieren zu können. Schlussendlich hat das Cloud Computing zusätzliches Potential für Datenanalysen und -verarbeitung geborgen, indem Infrastruktur nun in vielerlei Hinsicht günstig zur Verfügung steht und diverse Technologien mit geringer Einstiegsschwelle genutzt werden können. Aus der anfänglich monolithischen Welt der Datenbanken ist eine heterogene und dynamische Datenlandschaft geworden, die Data Engineers benötigt, um den Anforderungen gerecht zu werden.
Anhand von fünf Technologien soll mit diesem Artikel eine Orientierung zur Lösung aktueller Problemstellungen im Bereich “Data Engineering” gegeben werden. In den Gebieten “Batch Processing” und “Streaming” werden die etablierten Technologien Apache Spark und Apache Kafka vorgestellt. Mit Apache Airflow und der Serverless-Technologie Lambda werden zwei unterschiedliche Konzepte präsentiert, um Abläufe zu steuern. Zuletzt und entgegen der Stoßrichtung der Einleitung findet sich auch ein Kapitel über SQL und relationale Datenbanken.
* Unter Data Engineering ist hier ein sehr enger Begriff verstanden, nämlich die Tätigkeit, eine kontinuierliche Datenversorgung und -aufbereitung herzustellen.
SQL und relationale Datenbanksysteme
SQL ist eine Abkürzung für structured query language und ist fester Bestandteil von relationalen Datenbanksystemen. Entgegen des allgemeinen Tones und trotz diverser (Weiter-)Entwicklungen von NoSQL[1]-Lösungen, spielen SQL-Systeme weiterhin eine tragende Rolle in der modernen Datenarchitektur. Das zeigt auch eine Umfrage aus dem Jahr 2019, die sich folgendermaßen zusammenfassen lässt: Der Einsatz von NoSQL-Systemen ist in den allermeisten Fällen keine Abkehr, sondern ein Zusatz zu bestehenden Systemen, die auf SQL setzen.
Auch in modernen Frameworks spiegelt sich die Popularität von SQL wider: So zeugen die Entwicklung in Spark und Kafka (siehe beide in den nachstehenden Absätzen) vom Stellenwert, der SQL eingeräumt wird. Schließlich gibt es Tools, die SQL-Queries für NoSQL-Systeme kompatibel machen, hier beispielhaft zu nennen Apache Drill.
Ein Grund für die Popularität von SQL, neben seinen Vorzügen als einfach und semantisch an die englische Sprache angelehnt, liegt in seiner weiten Verbreitung. Auch außerhalb des Datenbankmilieus finden sich Analysten und Beschäftigte im Reporting, die SQL beherrschen.
SELECT
DEPARTMENT,
MANAGER,
COUNT(USER_ID)
FROM TBL_EMPLOYEE
WHERE IS_EXTERNAL = 1
GROUP BY DEPARTMENT, MANAGER
Klar lesbar führt diese Query eine Selektion, Filterung und Gruppierung von Daten aus.
Relationale Datenbanken sind eng mit SQL verbunden. Diese ausgereifte Technologie sticht vor allem durch Konsistenz und Dauerhaftigkeit hervor. Zudem muss das Tabellenschema vor dem ersten Schreiben definiert sein, was zwar zu Erwartbarkeit und Sicherheit führt, aber auch als aufwendig in der Verwaltung und starr angesehen werden kann. Der Umgang mit Daten, deren Struktur nicht explizit angegeben werden kann oder wechselhaft ist, kann sich also in relationalen Datenbanken als beschwerlich gestalten. Gleiches gilt für komplex-strukturierte Daten, da auch diese in Tabellen und Relationen eingepasst werden müssen, damit sie von der Datenbank effektiv behandelt werden können.
Vorzüge von relationalen Datenbanken:
- Eine seit den 1970er Jahren entwickelte und damit ausgereifte Technologie, die von vielen Experten beherrscht wird
- Eine starke Typisierung und der Definitionszwang a priori von Tabellenschemata garantieren Erwartbarkeit und Sicherheit
- Mit SQL eine weitverbreitete, verständliche Query-Sprache
- Die Implementierung des ACID-Schemas, das Konsistenz, Sicherheit und Dauerhaftigkeit von Datenständen garantiert
- Redundanzarmer Speicherverbrauch durch Normalisierung und Referenzmöglichkeiten
Spark
Fast schon ein Klassiker und nun bereits zur Version 3 gereift, ist Spark der Standard, um sehr große Datenvolumen effizient zu prozessieren. Die Performance steht vor allem auf zwei Säulen: Zum einen werden Verarbeitungsschritte auf eine Schar an Worker-Nodes verteilt, was große Datenmengen parallelisiert bearbeitbar macht. Zum anderen ist es ein intelligentes System, um Zwischenergebnisse im Arbeitsspeicher zu halten, um Berechnungsstrecken abzukürzen und Zugriffszeiten zu verkürzen.
Die Bearbeitung durch das Spark-Cluster wird erst dann ausgelöst, wenn die Strecke End-to-End (von den Ausgangsdaten über Transformationen bis zum Endprodukt) definiert ist. Wie angedeutet, versucht Spark die Aufgabenlast möglichst parallelisiert auf den Worker-Nodes auszuführen. Gleich einem Query-Optimizer in einer relationalen Datenbank, sucht Spark einen möglichst performanten Weg, die Definition in einzelne Schritte zu zerlegen und diese wiederum als Tasks an die Worker zu verteilen.
Anhand eines einfachen Beispiels soll dieses Schema veranschaulicht werden. Dabei wird angenommen, dass Daten aus einer großen Menge Dateien prozessiert werden, die in einem Textformat vorliegen. Es werden diverse Transformation durchgeführt, um die wichtigen Informationen zu extrahieren. Aus einer zweiten Datenquelle, die ebenfalls in einem rohen Zustand im Textformat vorliegt, sollen weitere Informationen entnommen werden und an das Zwischenprodukt angereichert werden.
Die unten dargestellte Abbildung, die Spark intern generiert, kann folgendes beobachtet werden:
Die Ausgangsdaten, die als Textdateien vorliegen, werden zu Anbeginn der Stages 8 und 10 eingelesen und anschließend transformiert, was sich in den Aktionen “map” und “distinct” widerspiegelt. Zuletzt werden die Ergebnisse mit einer “join” Aktion (wie man sie auch von SQL kennt) in Stage 10 vereint.
Ein weiterer Vorteil von Spark ist die Kompatibilität mit verschiedenen Datenformaten. So erlaubt Spark Lese- und Schreibvorgänge in unter anderem JSON, XML, CSV, Avro, kann aber auch ebenso gut mit Datenbankverbindungen umgehen – seien es nun klassische RDBMS oder Big-Data Datenbanken wie Hive.
Zwar basiert die Engine auf Java, längst aber hat Python (in Form der PySpark Bibliothek) als primäre Implementierungssprache die mit Abstand weiteste Verbreitung[2]. Entwicklungen wie die von Koalas, welches die beliebte Datenanalyse-Bibliothek Pandas in Spark integriert, unterstreicht dies zusätzlich. Weitere unterstütze Sprachen sind R, Scala und SQL.
Sparks primäre Stärke ist die Abarbeitungen definierter Prozessierungs-Jobs, in allen Facetten und Farben. Für Use-Cases hingegen, die geringe Latenzen auf Datenanfragen erwarten, sollte man besser auf andere Tools setzen, wie etwa Presto, Impala oder Dremio.
Vorzüge von Spark:
- Verarbeitet große Datenmengen (TB-Bereich) sehr effizient
- Skaliert durch Hinzufügen weiterer Worker Nodes zum Cluster
- Unterstützt viele Datenformate und Anbindungen zu Datenbanken
- Prozessierungs-Abfragen lassen sich in SQL, R, Scala, Java und Python schreiben
Spark gibt es in allen möglichen Ausgaben und Größen:
- Jupyter Notebooks mit PySpark und lokalem Spark Context (zum Beispiel als Docker-Container)
- On-premise Lösungen wie Cloudera oder Hortonworks
- Notebook zentrierte Cloud-Lösung von Databricks
- Cluster in der Cloud: EMR in AWS, HDInsights in Azure, Dataproc in GCP
Kafka
Kafka ist eine verteilte, fehler-tolerante und performante Streaming-Plattform. Wenn es darum geht, hohes Datenaufkommen in Echtzeit zu verarbeiten, kommt häufig Kafka ins Spiel. Die Plattform wird auch für Use Cases genutzt, in denen eine große Anzahl heterogener Systeme mit gegenseitigen Abhängigkeiten auftreten. Anders als bei gewöhnlichen Data Stores, ist ein Stream per Definition nicht erschöpft und Operationen darauf sind ebenso kontinuierlich.
Messages werden als Key-Value-Paare behandelt und in Topics gegliedert. Topics wiederum sind in Partitionen unterteilt, die redundant und damit gegen Ausfälle gesichert auf mehreren Nodes gehalten werden. Und schließlich gibt es noch die Teilnehmer an beiden Enden: Konsumenten (Consumers) und Produzenten (Producers), die aus den Topics lesen bzw. in jene schreiben.
In der Abbildung sieht man, welche Stelle Kafka im System einnimmt, nämlich als Datenhub: Von unten liefern die Quellsysteme hauptsächlich ins Kafka-System (Producer) und von oben werden die (mithin aufbereiteten) Streams konsumiert (Consumer).
Durch die Streaming API und die Entwicklung von ksql, das es ermöglicht, direkt SQL Queries auf Streams auszuführen, steht eine high-level Interaktionsmöglichkeit zur Verfügung, um mit Streams zu interagieren und aus bestehenden neue zu bauen. Dabei sind alle Transformationen vertreten, die man auch aus anderen Processing Frameworks (wie Spark) oder Datenbank-SQL-Dialekten kennt: Filtern, Gruppieren, Joinen, Mappen, etc. Darüber hinaus gibt es aber auch die Streams immanente Zeitachse, die mit Windowing Funktionen behandelt werden kann, also die Fragestellungen danach, wie oft Events pro Zeitabschnitt stattgefunden haben, z.B. Klicks auf Elementen von Web Pages.
Die Vorzüge von Kafka nochmal auf einem Blick:
- Ermöglicht Analysen und Operationen in real time
- Integriert heterogene Systeme zu einem zentralen Datenhub (Producer und Consumer API)
- Skalierbar und ausfallsicher durch redundantes Halten von Partitionen
- Stellt mit ksqldb eine Technologie bereit, um mit SQL Queries Streams zu transformieren
Ein gutes Starter-Kit findet man hier. In allen gängigen Cloud Plattformen gibt es auch Services (Amazon MSK, Azure HD Insight, …).
Airflow
Ein weiteres Themenfeld ist die Strukturierung des Ablaufs der einzelnen Datenaufbereitungs- und Datenverarbeitungsschritte. Besonders bei einer hohen Anzahl an beteiligten und einer hohen Heterogenität in den Komponenten ist es höchst ratsam ein Orchestrierungstool einzusetzen, um einen stabilen Ablauf zu garantieren. Für moderne Orchestrierungstools gibt es dabei einen breiten Anforderungskatalog, der in folgende Kategorien eingeteilt werden kann:
- Kontrollierbarkeit: Die beteiligten Komponenten und Schritte sind wohldefiniert. Die Ausführung soll nachvollziehbar sein, sowohl im Laufen als auch im Nachgang.
- Integrierbarkeit: Diverse und meist sehr heterogene Komponenten sollen ansprechbar und ihre Ausführung soll beobachtbar sein.
- Reaktivität: Ergebnisse einzelner Schritte sollen ausgewertet werden können und in den Ablaufprozess einfließen. So soll zum Beispiel definiert sein, was im Fall des Scheiterns eines ganzen Schrittes passieren soll: eine Wiederholung, eine Abweichung vom normalen Prozessweg, ein Ignorieren?
- Skalierbarkeit: Das Wachsen der Ablaufstruktur kann durch die Erweiterung der Ausführungsinfrastruktur möglichst einfach aufgefangen werden.
Apache Airflow erweist sich für diese Anforderungen als optimaler Kandidat. Komplexe Abläufe werden als directed acyclic graph (DAG) beschrieben, die Schritte sind als Knoten durch Bedingungen untereinander verknüpft. Diese DAGs bilden eine ausführbare Einheit, ihre Ausführung lässt sich automatisieren. Die Beschreibung wird rein in Python geschrieben. Das hat den Vorteil, dass die Entwicklung schnell geht und Operatoren gegebenenfalls leicht selbst geschrieben werden können. Zumeist ist das jedoch gar nicht nötig, da in Apache Airflow bereits mit eine Fülle an diversen und gängigen Operatoren implementiert ist. Darunter fallen: Triggern von Spark Jobs, Bereitstellung von Cloud Infrastruktur und das Ausführen von Datenbank-Queries.
Im Beispiel unten sieht man einen beispielhaften DAG-Ablauf, bei dem mehrere Stränge parallel ausgeführt werden und zuletzt zusammengeführt werden.
In der Abbildung sieht man, wie DAGs mehrere Richtungen nehmen können. Der letzte Schritt wertet die Ergebnisse von beiden Vorgängern aus und führt die Stränge wieder zusammen.
Beim Thema Skalierbarkeit liefert Airflow zwei Lösungswege mit: Der Celery Executor verteilt Tasks über einen Message Broker auf die dort registrierten Arbeiterprozesse. Kubernetes lässt sich als Ausführungsplattform über den Kubernetes Executor anschließen.
Zuletzt soll noch die Verwaltung von Zugängen zu Fremdsystemen (seien es nun Datenbanken oder Cloud Computing Instanzen) angesprochen werden. Dafür gibt es den sogenannten Secrets Manager, um übersichtlich, zentral und verschlüsselt die Verbindungsschlüssel zu den Systemen hinterlegen kann.
Die Vorzüge von Airflow auf einem Blick:
- Komplexe Abläufe werden in DAGs beschrieben und angesteuert
- Airflow bietet ein Füllhorn an Operatoren zur Kommunikation mit Fremdsystemen
- Die Ausführung kann skalierbar konfiguriert werden mithilfe von Celery und Kubernetes Executors
- Secrets von Datenbanken und anderen Systemen werden von Airflow zentral verwaltet und können im Code referenziert werden
Es gibt bereits fertige Docker Images, um Airflow auszuprobieren. Die Installation und Einrichtung ist aber auch recht einfach. Darüber hinaus bietet die Google Cloud auch den verwalteten Airflow Service Cloud Composer an, mit dem man sofort loslegen kann.
Serverless
Zuletzt soll hier noch ein komplett anderer Ansatz der Ablaufsteuerung angeführt werden, ein Ansatz der sich der Event-Driven Architektur zuordnen lässt. Statt einer zentralen Steuereinheit, die die Abläufe orchestriert, wird hierbei dezentral definiert, mit welchen Abläufen auf spezifische Events reagiert werden soll. Das kann zum Beispiel eine Veränderung im Data Lake sein, ein Infrastrukturevent wie die Bereitstellung einer Recheninstanz oder auch schlicht ein Zeit-Event. Auf Seiten der Funktionsaufrufe sind typische Beispiele das Anstoßen einer Data Ingestion, das Ausführen eines Tabellenupdates oder das Senden einer Benachrichtigung. Durch lokale Konfiguration der Reaktivität, wird hiermit also ein Ablaufkomplex zusammengebaut.
In der Abbildung sieht man das Schema der Serverless-Architektur, entnommen von OpenWhisk, einer Open-Source Serverless Plattform. Ein Feed stellt den Eventfluss zur Verfügung. Eine Regel verbindet das Auftreten eines Events (Trigger) mit der Aktion (Action).
Eine weitere Besonderheit ist das Serverless-Paradigma. Demnach entfällt der Aufwand für die Bereitstellung von Rechenressourcen zu sorgen. Stattdessen bestimmt die unterliegende Umgebung (also z.B. eine Cloud-Infrastruktur), wie Ressourcen allokiert werden und wo Code ausgeführt wird. Ein Vertreter einer solchen Applikation zur Verwaltung und Deployment von Ressourcen ist Kubernetes. Dem Entwickler wird also die Annehmlichkeit geboten, seinen Fokus ganz auf die Entwicklung zu legen. Zudem ist die Ausführung sprachagnostisch und alle gängigen modernen Programmiersprachen werden unterstützt. Damit können Ausführungen in verschiedenen Sprachen koexistieren und auch miteinander gekoppelt werden.
Einige Beispiele für den Einsatz:
- Eine neue Datei ist im ADL (Azure Data Lake) abgelegt worden und eine Data Ingestion soll angestoßen werden.
- Ein Tabellen-Inhalt der No-SQL-Datenbank DynamoDB wurde verändert und dies soll ins Archiv geschrieben werden (das entspricht Triggers in Datenbanken).
- Zeitereignisse: Jede Stunde sollen Daten zum Modelltraining bereitgestellt werden (vergleichbar mit cron jobs in der serverzentrierten Welt).
Die Vorteile nochmal auf einem Blick:
- Keine Aufwände für Infrastrukturbereitstellung, stattdessen Konzentration auf Implementierung der Funktionalität.
- Event-Driven statt zentraler Orchestrierung.
- Funktionen können in verschiedenen Programmiersprachen geschrieben werden .
- Funktionen verschiedener Sprache können koexistieren.
Die Anbieter findet man meistens in der Cloud: Lambda in AWS, Cloud Functions in GCP, Azure Functions in der Azure Cloud – ohne das Open-Source-Projekt OpenWhisk verschweigen zu wollen.
Zusammenfassung
Dem Wandel und den Brüchen zum Trotz bildet SQL weiterhin die zentrale Schnittstelle zu Daten, sowohl was Queries als auch die Prozessierung betrifft. Womöglich überdauert SQL sogar noch die Welt der relationalen Datenbanken, aus der sie ursprünglich kam.
Andererseits lässt sich ebenso feststellen, dass es längst nicht mehr genügt, SQL zu beherrschen, um modernen Problemstellungen gerecht zu werden, was in der vormaligen Welt der Data Warehouses galt. Verlässt man den bereitgestellten Standard an Funktionalitäten in Spark oder Kafka, den SQL mitliefert, um spezifische Funktionen zu implementieren (man spricht hier von User Defined Functions), so benötigt es zusätzliches Wissen in Programmiersprachen wie Python oder Java. Ebenso erfordert der Umgang mit Airflow oder der Serverless-Technologie, wie in den vorherigen Abschnitten gezeigt wurde, die Beherrschung der Programmiersprache Python – oder im Fall von Serverless der einer anderen modernen Sprache.
Wählt man fünf Technologien aus, fallen im Gegenzug andere herunter, die dennoch hier Erwähnung finden sollen:
- MongoDB als Vertreter von Dokumentdatenbanken (NoSQL), die mit Flexibilität und Skalierbarkeit glänzen können.
- Apache Cassandra als Vertreter von Key-Value-Datenbanken (NoSQL), das vor allem für hohe Skalierbarkeit steht.
- Apache Flink als Data Streaming Framework in Ergänzung zu Apache Kafka.
- Apache Beam als Abstraktionsschicht zur Datenpipeline-Definition, die dann in Apache Flink oder in Apache Spark ausgeführt werden können.
[1] NoSQL bedeutet in erster Linie die Abkehr vom SQL Paradigma und solche Datenbanken bilden keine homogene Klasse. Stattdessen kann man grob drei Unterklassen definieren: Graphendatenbanken, Dokumentendatenbanken und Key-Value-Datenbanken.