Ga naar hoofdinhoud

Apache Spark-architectuur: een gids voor dataprofessionals

Begrijp hoe Apache Spark data op schaal verwerkt—van de fundamentele componenten tot de geavanceerde features die moderne bigdata-workflows aandrijven.
Bijgewerkt 16 apr 2026  · 15 min lezen

Heb je ooit geprobeerd een Spark-job te debuggen die ineens faalde, om er vervolgens achter te komen dat je compleet verdwaald raakte in het konijnenhol dat Spark heet? 

Toen ik voor het eerst met Apache Spark werkte, dacht ik dat ik alleen een paar PySpark-transformaties hoefde te schrijven en dat Spark vervolgens “magisch” zou schalen over het cluster. Ik had het mis. De performance van Spark hangt volledig af van je begrip van wat er achter de schermen gebeurt.

Deze gids is voor iedereen die Spark niet als een black box wil behandelen. We lopen door hoe de architectuur van Spark is ontworpen, van het master-worker-model en de uitvoeringsworkflow tot aan het geheugenbeheer en de fouttolerantiemechanismen. 

Als je snelle, fouttolerante en efficiënte bigdata-applicaties wilt bouwen, ben je hier aan het juiste adres!

Fundamentele architectuur van Apache Spark

Nog voordat je je eerste regel PySpark schrijft, heeft Spark al een paar architecturale keuzes voor je gemaakt. Spark is niet alleen snel dankzij in-memory computing, maar ook omdat het is gebouwd op een master-worker-architectuur die schaalt en bestand is tegen echte chaos, zoals node-crashes, Java Virtual Machine (JVM)-problemen en inconsistente datavolumes.

Laten we de kernarchitectuur van Spark ontleden en bekijken waarom die nog steeds zo krachtig is en een vaste waarde in moderne bigdata-workflows.

Master-workerparadigma

In de kern van Spark staat het master-worker model. Denk er zo over:

  • Driver (master): Dit is het brein van Spark. Hij voert je main()-functie uit, maakt de Spark-context, handelt de DAG-scheduling af en vertelt het cluster wat het moet doen.
  • Executors (workers): Dit zijn de spieren. Ze voeren je taken uit, houden data in het geheugen en rapporteren terug aan de driver.

Met deze opzet kun jij je focussen op het definiëren van transformaties, en Spark beslist waar en hoe die parallel op de executors worden uitgevoerd. 

Wat ik fijn vind aan dit ontwerp is dat het deployment-agnostisch is. Dezelfde code draait, of je nu lokaal, op Kubernetes of op Mesos uitrolt. Dat maakt lokaal ontwikkelen en testen makkelijk, waarna je kunt opschalen naar clusters zonder je code te herschrijven.

En hier is nog een krachtig voordeel van de scheiding tussen driver en worker: het verbetert foutisolatie. Als een workernode uitvalt tijdens een taak, kan Spark die taak aan een andere worker toewijzen zonder dat je applicatie crasht.

Kernonderdelen

Laten we uitpluizen wat er binnen de driver en de nodes gebeurt. 

Diagram dat de Spark-architectuur toont

Spark-architectuur. Afbeelding door de auteur.

Spark-context

Wanneer je SparkContext() aanroept of SparkSession.builder.getOrCreate() gebruikt, open je de toegangspoort tot alle interne magie van Spark.

De Spark-context:

  • Verbindt met je cluster manager
  • Wijst executors toe
  • Houdt de status van jobs en uitvoeringsplannen bij

Spark bouwt achter de schermen een Directed Acyclic Graph (DAG) van transformaties. Die DAG wordt opgesplitst in stages en taken en vervolgens parallel uitgevoerd.

De DAG-scheduler bepaalt welke taken samen kunnen draaien, en de Task-scheduler wijst ze toe aan executors. Ondertussen zorgt de Block manager ervoor dat data naar behoefte wordt gecachet, geshuffled of opnieuw geladen. 

Dit gelaagde ontwerp maakt Spark enorm flexibel, omdat je geheugen, opslag en compute onafhankelijk kunt afstellen.

Werk je met Spark-transformaties of feature engineering, bekijk dan Feature Engineering with PySpark om deze architectuur in actie te zien.

Executor-runtime

Executors zijn waar het werk gebeurt.

Elke executor draait:

  • Een of meer taken (ge-thread)
  • Een portie geheugen voor het cachen van data en shuffle-uitvoer
  • Zijn eigen JVM-instantie, geïsoleerd van de rest

Je kunt configureren hoeveel geheugen elke executor krijgt, hoeveel cores hij gebruikt en of hij naar schijf moet schrijven wanneer het geheugen opraakt. 

Maar wees voorzichtig: als je niet genoeg geheugen toewijst, krijg je voortdurend out-of-memory-fouten. Tegelijk moet je ook niet te veel geheugen toewijzen, want dat verspilt resources. Monitoren en tunen zijn hier essentieel.

Uitvoeringsworkflow: van code naar cluster

PySpark-code schrijven voelt vrij eenvoudig. Je filtert een DataFrame, doet een join, aggregeert iets en drukt op run. Maar achter die cleane API start Spark stilletjes een execution engine op die werk over meerdere nodes kan verdelen. 

Laten we doorlopen wat er achter de schermen gebeurt.

Conversie van logisch naar fysiek plan

Dit beseffen de meeste Spark-gebruikers in het begin niet: wanneer je PySpark-code schrijft, draait er niet meteen iets. Je bouwt een plan, en de Catalyst Optimizer van Spark neemt dat plan en zet het om in een efficiënt uitvoeringsplan.

Dat werkt in vier fasen:

  1. Analyse: Spark lost kolomnamen, datatypen en tabelverwijzingen op en controleert of alles geldig is.
  2. Logische optimalisatie: Hier past Spark regels toe zoals predicate pushdown en constant folding. Het optimaliseert filters en combineert projecties.
  3. Fysieke planning: Spark bekijkt meerdere uitvoeringsstrategieën en kiest de efficiëntste (op basis van datagrootte, partitionering, enz.).
  4. Codegeneratie: Tot slot gebruikt het whole-stage code generation om JVM-bytecode te produceren. 

Afbeelding met een diagram van Spark’s Catalyst Optimizer

Spark’s Catalyst Optimizer. Afbeelding door Databricks.

Die keten van .select(), .join() en .groupBy() draait dus niet simpelweg regel voor regel. Hij wordt geanalyseerd, geoptimaliseerd en gecompileerd tot iets dat snel op een cluster draait.

Bekijk deze PySpark-cheatsheet als je een spiekbrief nodig hebt voor de meest gebruikte PySpark-commando’s.

DAG-scheduler & aanmaken van stages

Wanneer het plan klaar is, neemt de DAG-scheduler het over.

Die breekt de job op in stages op basis van shuffle-grenzen, waarbij Spark bepaalt wat sequentieel moet gebeuren en wat parallel kan worden uitgevoerd.

Er zijn twee hoofdtypen stages:

  • ShuffleMapStage: Hierbij vindt een shuffle plaats, meestal veroorzaakt door brede transformaties zoals groupBy() of join(). De data wordt dan gepartitioneerd en over het netwerk verzonden. Dit stagetype is nodig om de ResultStage te berekenen.
  • ResultStage: Deze stages produceren output, zoals schrijven naar schijf of resultaten teruggeven aan de driver.

Eén belangrijk inzicht dat ik heb geleerd: minimaliseer shuffles. Een shuffle moet plaatsvinden voordat een stage eindigt en is duur. Je moet begrijpen waar ze in je DAG voorkomen en of je je code verder kunt optimaliseren om het aantal shuffles te verminderen. 

Levenscyclus van taakuitvoering

Zodra de DAG-scheduler alle stages heeft aangemaakt, kunnen ze op de verschillende executors worden uitgevoerd. 

De levenscyclus van taakuitvoering ziet er ongeveer zo uit:

  1. Task-serialisatie: De driver serialiseert taak-instructies en stuurt ze naar de executors.
  2. Shuffle write-fase: Spark schrijft de gepartitioneerde output naar de lokale schijf.
  3. Fetch-fase: Executors in de volgende stage halen de relevante shuffle-bestanden op van anderen in het cluster.
  4. Deserialisatie en uitvoering: Executors deserialiseren de data, draaien je logica en cachen of schrijven mogelijk resultaten weg.
  5. Garbage collection: De JVM herwint automatisch geheugen dat niet langer door Spark-applicaties wordt gebruikt. Deze stap is essentieel om memory leaks te voorkomen en te zorgen dat Spark-applicaties soepel draaien.

Een kleine tip uit eigen ervaring: als je Spark-job blijft hangen nadat hij eerder prima draaide, komt dat vaak door garbage collection of shuffle-fetchvertragingen. Controleer altijd je code en zorg dat je de architectuur van Spark begrijpt, zodat je deze onderwerpen effectief kunt optimaliseren.

Architectuur voor geheugenbeheer

Het geheugenbeheer van Spark is een zeer complex onderwerp en kan je uren debuggen kosten als je het niet begrijpt. 

Laten we daarom bekijken hoe Spark onder de motorkap met geheugen omgaat, zodat je hiervan op de hoogte bent en uren debuggen van trage code of out-of-memory-fouten kunt voorkomen.

Unified memory model

Vóór Spark 1.6 was het geheugen strikt verdeeld tussen execution (voor shuffles en joins) en storage (voor caching). Dat veranderde met Spark 1.6, met de introductie van het unified memory model. 

In het unified memory model wordt geheugen verdeeld over drie kernpools:

  • Gereserveerd geheugen: Een kleine hoeveelheid geheugen wordt gebruikt voor Spark-internals en het systeem. 
  • Spark-geheugen: Dit wordt gebruikt voor het opslaan van execution-data en voor caching. Het wordt dynamisch gedeeld. Als je job meer geheugen nodig heeft voor shuffles en minder voor caching (of andersom), past Spark zich aan.
  • User memory: Ruimte voor door de gebruiker gedefinieerde datastructuren die nodig zijn voor het uitvoeren van user code binnen Spark-applicaties.

De Spark-geheugenpool is verder opgesplitst in twee pools:

  1. Executor memory: Slaat tijdelijke data op die nodig is tijdens fasen van taakverwerking (bijv. shuffles, joins, aggregaties, …). 
  2. Storage memory pool: Gebruikt voor het cachen van data en het opslaan van interne datastructuren. 

Deze elasticiteit maakt Spark flexibeler bij onvoorspelbare datavolumes. 

Maar dit betekent ook dat je wat controle verliest als je niet weet wat er gebeurt. Als je bijvoorbeeld een groot DataFrame cache()t maar in dezelfde stage ook dure aggregaties hebt, kan Spark je gecachete data verdringen om ruimte te maken voor de shuffle.

Off-heap & kolomgebaseerde opslag

Bij Spark’s off-heap- en kolomgebaseerde opslag komt de Tungsten-engine om de hoek kijken. 

Tungsten introduceerde verschillende optimalisaties die de performance van Spark verbeterden:

  • Off-heap-geheugenbeheer: Spark slaat nu een deel van de data buiten de JVM-heap op, wat de garbage collection-overhead vermindert en het geheugenbeheer voorspelbaarder maakt.
  • Binaire formaatopslag: Data wordt opgeslagen in een compact, cache-vriendelijk binair formaat, wat het CPU-gebruik verbetert en vectorized execution mogelijk maakt.
  • Cache-bewuste algoritmes: Spark kan CPU-caches effectiever gebruiken en onnodige reads uit RAM of schijf vermijden.

En als je met DataFrames werkt, profiteer je hier onder de motorkap al van. Dat is een van de redenen waarom ik mensen aanraad DataFrames en SQL-API’s te gebruiken in plaats van ruwe RDD’s. Je krijgt de volledige kracht van Catalyst en Tungsten zonder extra tuning.

Als je met datacleaning-pijplijnen werkt, zie je dit in actie in Cleaning Data with PySpark.

Mechanismen voor fouttolerantie

Als je met gedistribueerde systemen werkt, weet je één ding zeker: ze vallen uit. Nodes crashen. Netwerkfouten gebeuren. Executors raken door geheugen heen en stoppen.

Maar Spark is gebouwd om deze problemen aan te kunnen en ervoor te zorgen dat je jobs toch slagen. 

Laten we dieper ingaan op hoe Spark ervoor zorgt dat je jobs slagen, zelfs als er instabiliteiten optreden.

RDD-lineage-tracking

Resilient Distributed Datasets (RDD’s) zijn de fundamentele datastructuur in Spark. En ze heten niet voor niets resilient. 

Spark gebruikt lineage om te garanderen dat elke RDD opnieuw kan worden berekend bij node-falen en dataverlies. 

Dus wanneer een node faalt, herberekent Spark simpelweg de verloren data met behulp van de lineage-grafiek. 

Zo werkt het in de praktijk: 

  • Narrow dependencies (zoals map() of filter()): Spark heeft alleen de verloren partitie nodig om te herberekenen.
  • Wide dependencies (zoals groupBy() of join()): Spark moet mogelijk data uit meerdere partities ophalen, omdat de output van meerdere stages nodig kan zijn. 

Lineage voorkomt dat je fouten handmatig moet afhandelen. Als je lineage-grafiek echter te lang wordt, met honderden transformaties, wordt het herberekenen van verloren data duur. Dan komt checkpointing in beeld.

Checkpointing & write-ahead logs

Bij complexe workflows of streaming-jobs kan Spark niet uitsluitend op lineage vertrouwen. Dan gebruik je checkpointing.

Je kunt rdd.checkpoint() aanroepen om de huidige RDD-state op te slaan op een betrouwbare opslaglocatie (zoals HDFS). 

Spark kapt vervolgens de lineage af. Als er een fout optreedt, wordt de data direct herladen in plaats van herberekend.

In structured streaming gebruikt Spark ook write-ahead logs (WAL’s) om te zorgen dat data niet verloren gaat tijdens transport. 

Dit maakt het zo stabiel: 

  • Betrouwbare receivers: Ze schrijven inkomende data naar logs voordat ze verwerken.
  • Executor-heartbeats: Deze regelmatige signalen bevestigen dat executors actief en gezond zijn.
  • Checkpoint-directories: Voor streaming-jobs bevatten ze offsets, metadata en outputstate, zodat je kunt hervatten waar je was gebleven.

Checkpointing is optioneel voor batch-jobs, maar vereist voor streaming-pijplijnen. 

Stel dat je een Spark-job had die faalde na 10 uur draaien, maar je kunt dankzij checkpointing en WAL’s gewoon doorgaan waar je was gebleven. 

Geavanceerde architectuurfeatures

Je hebt nu gezien hoe Spark jobs verwerkt en hoe het omgaat met geheugen en fouten.

In deze sectie duiken we in enkele geavanceerde architectuurupgrades die Spark dynamischer, meer realtime en beter aanpasbaar maken.

Adaptive query execution (AQE)

AQE is geïntroduceerd in Spark 3.0 en verbetert queryperformance door uitvoeringsplannen tijdens runtime dynamisch aan te passen op basis van statistieken die tijdens de uitvoering worden verzameld.

Features van AQE zijn onder meer:

  • Dynamisch van join-strategie wisselen: Als je broadcast join niet in het geheugen past, schakelt AQE over naar een sort-merge join.
  • Shuffle-partities samenvoegen: Kleine shuffle-partities samenvoegen tot grotere, wat de overhead verlaagt.
  • Omgaan met scheve data: AQE kan scheve partities opsplitsen om de uitvoeringstijd in balans te brengen.

Deze feature is een gamechanger, omdat jobs die eerder handmatige tuning en trial-and-error vereisten nu realtime kunnen aanpassen.

Zorg er wel voor dat je het expliciet inschakelt via de configuratie (spark.sql.adaptive.enabled = true). En als je op Spark 3.0+ draait, is er geen reden om het niet te gebruiken.

Structured streaming-architectuur

Structured Streaming neemt de engine van Spark en breidt die uit naar het realtime-domein, zonder dat je een compleet nieuwe API hoeft te leren.

Achter de schermen past het nog steeds micro-batching toe. Maar het handelt ook af:

  • Offsetbeheer: Spark houdt precies bij welke data er uit je bron is gelezen (Kafka, socket, bestand, enz.). Dit biedt sterke exactly-once-garanties bij correcte configuratie.
  • Watermarking: Bij tijdgebaseerde aggregaties gebruikt Spark watermarks om te beslissen wanneer late data te laat is om nog mee te nemen. Dit is cruciaal voor event-time-verwerking.
  • State stores: Bij windowed aggregations of streaming joins onderhoudt Spark state over micro-batches heen. Deze state wordt op schijf opgeslagen en gecheckpoint om dataverlies te voorkomen.

Het krachtige hier is dat streaming aanvoelt als batchen. Je schrijft een groupBy() of een filter() en Spark regelt de rest, waardoor streaming-analytics toegankelijk wordt zonder gespecialiseerde toolchain.

Beveiligingsarchitectuur

Draai je Spark in productie, zeker in finance, healthcare of vergelijkbare sectoren, dan moet je weten hoe Spark authenticatie, encryptie en auditability afhandelt.

Dus laten we dieper in deze onderwerpen duiken en zien hoe Spark ze aanpakt.

Authenticatie & encryptie

Spark heeft veel securityfeatures die je eerst moet inschakelen. Maar eenmaal geactiveerd, biedt Spark een solide toolbox voor veilige communicatie en authenticatie: 

  • Authenticatie (SASL): Spark gebruikt de Simple Authentication and Security Layer (SASL) om te verifiëren dat alleen geautoriseerde gebruikers en services jobs kunnen indienen of met het cluster kunnen verbinden.
  • Encryptie in transit (AES-GCM, SSL/TLS): Spark versleutelt communicatie tussen nodes met AES-GCM (geauthenticeerde encryptie) of TLS. Dit beschermt jobdata tegen sniffen, vooral belangrijk in multi-tenant- of cloudomgevingen.
  • Kerberos-integratie: Als je op Hadoop/YARN draait, integreert Spark met Kerberos voor veilige gebruikersauthenticatie. Dit koppelt je Spark-jobs direct aan enterprise identity- en accessmanagementsystemen.
  • UI-toegangscontrole: De Spark Web UI kan gevoelige info lekken (zoals logs, inputpaden, SQL-queries), dus zet spark.acls.enable=true en spark.ui.view.acls en spark.ui.view.acls.groups om dit te beperken.

Je kunt alle securityfeatures nalezen in de officiële documentatie van Spark. Bekijk die en zorg dat je de features inschakelt die je Spark-applicaties moeten beveiligen.

Audit & compliance

Loggen wie wat en wanneer heeft gedaan, is ook cruciaal. 

Spark ondersteunt: 

  • Event logging: Wanneer ingeschakeld (spark.eventLog.enabled=true), registreert Spark elk job-, stage- en taktevent op schijf. Je kunt deze logs gebruiken om jobhistorie te reproduceren of aan auditvereisten te voldoen.
  • Role-based access control (RBAC): Spark biedt zelf geen RBAC, maar als je Spark gebruikt via een platform als Databricks, EMR of OpenShift, krijg je meestal RBAC op de infrastructuurlaag. Spark dient jobs in met een gedefinieerde identiteit, die toegang tot zowel data als compute resources regelt.
  • Datamasking en toegangscontrole bij de bron: Spark leest uit veel bronnen (Parquet, Delta Lake, Hive, enz.), en je toegangscontrole moet daar worden afgedwongen.

Patronen voor performance-optimalisatie

Spark is behoorlijk krachtig en snel, en kan nog sneller worden geoptimaliseerd als je weet waar je de juiste aanpassingen doet. 

Er zijn verschillende gebieden waarop je kunt optimaliseren om het maximale uit Spark te halen. Laten we elk gebied uitdiepen.

Shuffle-optimalisatie

Als Spark een zwak punt heeft, is het de shuffle. Shuffles gebeuren wanneer data tussen partities moet worden verplaatst, meestal na brede transformaties zoals groupByKey(), distinct() of join().

En als shuffles misgaan, krijg je enorme schijf-I/O, lange garbage collection-pauzes of scheve taken die nooit afronden. 

Zo verbeter je shuffles:

  • Geef de voorkeur aan reduceByKey() boven groupByKey(): reduceByKey() aggregeert lokaal vóór de shuffle. groupByKey() stuurt alles over het netwerk.
  • Herpartitioneer slim: Gebruik .repartition(n) om de paralleliteit te verhogen, of .coalesce(n) om die te verlagen. Laat het niet aan de standaardpartitionering van Spark over.
  • Gebruik broadcast joins (wijs): Als één dataset klein genoeg is, broadcast die dan naar alle workers. Stel spark.sql.autoBroadcastJoinThreshold in om de groottegrens te bepalen.
  • Vermijd collect(): Vermijd dit waar mogelijk, want data naar de driver trekken sloopt de performance.

Richtlijnen voor geheugenconfiguratie

Het tunen van het geheugen van Spark is bijna een wetenschap, maar met de checklist hieronder wordt het makkelijker:

  • Wijs voldoende geheugen toe: Begin met minstens 6 GB geheugen voor het Spark-cluster en pas aan op basis van je specifieke behoeften.
  • Overweeg de Spark memory fraction: Standaard is 60% de memory fraction in Spark. Verhoog deze als je applicaties zwaar leunen op DataFrame/Dataset-operaties of als je meer user memory nodig hebt. 
  • Gebruik het juiste aantal cores per executor: Meestal is 3–5 optimaal. Te weinig leidt tot onderbenutting, te veel tot taakcontentie.
  • Schakel dynamische allocatie in (indien ondersteund): Spark kan executors op- en afschalen op basis van de workload. 
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=2
spark.dynamicAllocation.maxExecutors=20
  • Pas de storage fraction aan: Als je meer caching nodig hebt, verhoog dan de waarde van spark.memory.storageFraction.
  • Monitor en profileer geheugengebruik: Gebruik tools zoals de Spark UI of VisualVM om geheugengebruik te volgen en knelpunten op te sporen.

Het aanpassen van de geheugenconfiguratie kan enorm helpen. Ik heb ooit een job van 30 minuten teruggebracht naar 8 minuten door de geheugenconfiguratie aan te passen, zonder een enkele regel code te veranderen.

Formules voor clustersizing

Dit is het onderdeel waar de meeste teams de fout in gaan, omdat ze de clusteromvang gokken in plaats van die correct te schatten. 

Maar je kunt beter doen met de onderstaande formules: 

  1. Bepaal het aantal partities: 
    • Bereken het aantal benodigde partities op basis van je datagrootte en gewenste partitie-grootte. 
    • Een vuistregel is één partitie per 128 MB tot 256 MB ongecomprimeerde data.
    • Formule: Aantal partities = Afronden naar boven(Totale datagrootte ÷ Partitiegrootte).
  2. Bereken het totale aantal cores: 
    • Het benodigde aantal cores moet voldoende zijn om alle partities parallel te verwerken.
    • Formule: Totale cores = Afronden naar boven(Aantal partities ÷ Partities per core).
  3. Bepaal geheugen per executor: 
    • Bereken hoeveel geheugen elke executor nodig heeft op basis van zijn cores, partitiegrootte en overhead.
    • Formule: Geheugen per executor = Basisgeheugen × (1 + overheadpercentage).
  4. Bereken het aantal executors: 
    • Bepaal het aantal executors op basis van het totale aantal cores en cores per executor.
    • Formule: Aantal executors = Afronden naar boven(Totale cores ÷ Cores per executor).
  5. Bereken totaal geheugen: 
    • Bereken het totale geheugen dat voor het cluster nodig is op basis van het aantal executors en geheugen per executor.
    • Formule: Totaal geheugen = Aantal executors × Geheugen per executor.

Bijvoorbeeld: 

  • Input: 500GB aan data en een partitiegrootte van ~128MB
  • Partities: ~4.000 partities
  • Cores: 4.000 partities / 4 partities per core = 1.000
  • Geheugen per executor: Stel 8 GB per executor en 20% overhead. 8 GB * 1,20 = 9,6 GB
  • Executors: 1.000 cores / 4 cores per executor = 250 executors
  • Totaal geheugen: 250 executors * 9,6GB = 2.400 GB

Maar onthoud: dit is slechts een schatting. Gebruik het als startpunt en optimaliseer verder via profilering.

Opkomende architectuurtrends

Spark bestaat al een decennium, maar is nog steeds actueel. Het evolueert sneller dan ooit dankzij cloud-native platforms, GPU-acceleratie en nauwere ML-integratie.

Als je Spark vandaag nog op dezelfde manier gebruikt als drie jaar geleden, laat je waarschijnlijk performance liggen en mis je mooie nieuwe features.

Laten we enkele van de nieuwste bekijken.

Photon-engine (Databricks)

Als je met Databricks werkt, heb je waarschijnlijk al met Photon gewerkt en erover gehoord.

Wil je meer leren over Databricks, dan raad ik de cursus Introduction to Databricks aan.

Photon is de next-gen engine op het Databricks Lakehouse-platform die snelle queryperformance levert tegen lage kosten. Hij is compatibel met Spark-API’s, dus je hoeft je Spark-code niet aan te passen om ervan te profiteren. 

Het helpt je SQL- en PySpark-code aanzienlijk te versnellen.

Photon bevat de volgende features: 

  • Vectorized execution: Photon verwerkt data in columnar batches en benut SIMD (Single Instruction, Multiple Data) CPU-instructies om operaties op meerdere waarden tegelijk uit te voeren. Traditioneel Spark gebruikt row-by-row-executie en leunt zwaar op de JVM voor geheugentoewijzing en garbage collection.
  • C++-runtime (geen JVM-overhead): Geen Java garbage collection, wat een bottleneck kan zijn in grote Spark-jobs. Geheugen wordt nauwkeurig beheerd in C++.
  • Verbeterde query-optimalisaties: Photon integreert diep met de Catalyst Optimizer van Spark, maar voegt ook eigen optimalisaties toe tijdens uitvoering (zoals runtime filtering, adaptieve codepaden, join- en aggregatie-optimalisaties). 
  • Hardwareversnelling: Ondersteuning voor moderne hardware (zoals NVIDIA GPU’s, AVX-512-instructiesets voor Intel-CPU’s, Graviton (ARM)-processors op AWS). 

Serverless Spark

Serverless is fantastisch, omdat je geen clusters hoeft te beheren, geen resources vooraf hoeft te provisionen en je alleen betaalt voor de tijd dat Spark draait. 

En serverless voor Spark is al beschikbaar in diensten zoals Databricks Serverless, AWS Glue en GCP Dataproc Serverless.

En hierom is het geweldig:

  • Automatische schaalbaarheid: Het platform schaalt compute op basis van de daadwerkelijke behoeften van je job, waardoor je niet hoeft te gokken hoeveel nodes je nodig hebt.
  • Kosteneffectiviteit: Je betaalt alleen voor wat je gebruikt. Nooit meer betalen voor idle servers. 
  • Eenvoud: Geen gedoe met cluster-setup, configuratie of onderhoud—dat wordt voor je geregeld.
  • Performance: Snellere uitvoeringstijden zijn mogelijk omdat de configuratie en setup voor je zijn geoptimaliseerd.

Serverless Spark is ideaal voor interactieve analytics, ad-hocjobs of onvoorspelbare workloads.

Maar let op: langlopende, consistente pijplijnen kunnen nog steeds goedkoper zijn op vaste clusters. Meet altijd zowel kosten als latency.

MLflow-integratie

Nu de industrie verschuift, vervaagt de lijn tussen data-engineering en AI. Zoals Deepak Goyal, CEO & Founder bij Azurelib Academy, besprak in de DataFramed-podcast

Data-engineering gaat een vitale en fundamentele rol spelen in de komende verschuiving naar AI.

Deepak GoyalCEO & Founder at Azurelib Academy

Als je machine learning op schaal doet en modellen in productie wilt brengen, is Spark alleen niet genoeg. Je hebt MLOps-principes nodig, zoals experimenttracking, modelversiebeheer en reproduceerbaarheid. Daar komt MLflow in beeld. 

MLflow integreert nu met Spark en brengt een volledige MLOps-stack naar je pijplijnen.

Je kunt: 

  • Experimenten tracken: Log parameters, metrics en artifacts van Spark ML-jobs met mlflow.log_param() en mlflow.log_metric().
  • Modellen versies geven: Sla modellen van pyspark.ml of sklearn rechtstreeks op in het modelregister van MLflow.
  • Modellen serven: Geleerde modellen uitrollen naar REST-endpoints met de model serving van MLflow.

Je hoeft niet van tools te wisselen. Je blijft Spark gebruiken voor training, feature engineering en scoring, terwijl je MLflow inzet voor MLOps-taken.

Conclusie

Als je niet veel van Spark weet, voelt het als een enorme black box. Je schrijft wat PySpark-code, drukt op run en hoopt dat het werkt. 

Soms werkte dat voor mij, soms leidde het tot lange debugsessies om uit te zoeken wat er misging. 

Pas toen ik achter de schermen ging kijken, viel alles op zijn plek. En het duurde best lang voordat ik begreep wat er gaande is.

Hierop zou ik me focussen als ik weer vanaf nul begon: 

  • Leer hoe Spark je code opbreekt in jobs, stages en taken.
  • Begrijp het geheugen.
  • Let op shuffles.
  • Begin klein en draai dingen in local mode. Handen vuil maken.

Dat is precies wat we in dit artikel hebben geleerd.

Wil je blijven leren, dan raad ik deze beginnersvriendelijke bronnen aan:

FAQs

Hoe kies ik de juiste cluster manager voor mijn Spark-deployment?

Spark ondersteunt meerdere cluster managers (YARN, Mesos, Kubernetes en standalone). Je keuze hangt af van bestaande infrastructuur, behoefte aan het delen van resources en operationele expertise: YARN integreert goed op Hadoop-clusters, Kubernetes biedt containerized portabiliteit en Mesos blinkt uit in multi-tenant-isolatie.

Wat is de external shuffle service en hoe verbetert die de performance?

De external shuffle service koppelt het serveren van shuffle-bestanden los van de levenscyclus van executors, waardoor dynamische allocatie mogelijk wordt en dataverlies bij het beëindigen van executors afneemt. Shuffle-bestanden blijven beschikbaar, zelfs nadat executors zijn afgesloten, wat stage-retries versnelt en schijf-I/O onder zware belasting beperkt.

Hoe werken broadcast joins intern en wanneer moet ik ze gebruiken?

Bij broadcast joins stuurt Spark een kleine lookuptabel naar elke executor om volledige datashuffles te vermijden. Gebruik ze wanneer één kant van de join onder de spark.sql.autoBroadcastJoinThreshold (standaard 10 MB) blijft, omdat ze het netwerk-I/O drastisch verminderen en joins op scheve sleuteldistributies versnellen.

Wat zijn best practices voor het tunen van JVM garbage collection in Spark?

Monitor GC-pauzes via de Spark UI of tools zoals VisualVM en geef de voorkeur aan de G1GC-collector vanwege de lage pauzetijden. Wijs executorgeheugen toe met ruimte voor overhead (spark.executor.memoryOverhead) en tune -XX:InitiatingHeapOccupancyPercent om GC eerder te triggeren en lange stop-the-world-pauzes te voorkomen.

Hoe kan ik GPU-acceleratie inzetten om Spark-jobs te versnellen?

Gebruik de NVIDIA RAPIDS Accelerator for Apache Spark om SQL- en DataFrame-operaties transparant naar GPU’s te offloaden. Deze haakt in op Spark’s execution engine en vervangt CPU-gebaseerde operators door GPU-versnelde equivalenten, wat tot 10× snellere verwerking oplevert voor geschikte workloads.

Wat is het verschil tussen statische en dynamische resource-allocatie in Spark?

Statische allocatie fixeert het aantal executors voor de levensduur van de job en biedt voorspelbaarheid, maar kan tot idle resources leiden. Dynamische allocatie laat Spark executors aanvragen of vrijgeven op basis van openstaande taken en workload, wat het cluster beter benut bij fluctuerende jobs—ideaal in gedeelde omgevingen.

Hoe configureer ik Spark voor optimale performance op cloudopslagsystemen zoals S3?

Schakel S3 transfer acceleration in, tune spark.hadoop.fs.s3a.connection.maximum en gebruik consistent view (S3A v2) om met eventual consistency om te gaan. Voorkom kleine bestanden door te coalescen vóór het schrijven en overweeg de S3A committers om list-operatie-overhead te verminderen en de schrijfsnelheid te verbeteren.

Hoe kan ik Spark-communicatie beveiligen met Kerberos en TLS?

Schakel TLS in voor RPC (spark.ssl.enabled) en configureer SASL/Kerberos (spark.authenticate and spark.kerberos.keytab) om wederzijdse authenticatie af te dwingen. Sla inloggegevens op in een veilige, via HDFS toegankelijke keytab en beperk toegang tot de Spark UI via ACL-instellingen om ongeautoriseerde blootstelling van data te voorkomen.

Wat zijn Pandas UDF’s en wanneer zijn ze efficiënter dan gewone UDF’s?

Pandas UDF’s (vectorized UDF’s) gebruiken Apache Arrow om data in batches uit te wisselen tussen de JVM en Python, wat de serialisatie-overhead drastisch vermindert. Ze presteren beter dan traditionele row-by-row UDF’s voor complexe numerieke operaties, vooral bij het verwerken van grote kolombatches in PySpark.

Welke voordelen biedt de DataSource V2-API ten opzichte van V1 voor aangepaste databronnen?

DataSource V2 biedt een schonere, meer modulaire interface die push-downfilters, partition pruning en streamingbronnen native ondersteunt. Het maakt fijnmazige lees-/schrijfregrip mogelijk en een betere integratie met de Catalyst-optimizer van Spark, wat resulteert in hogere performance en eenvoudigere onderhoudbaarheid voor maatwerkconnectoren.


Patrick Brus's photo
Author
Patrick Brus
LinkedIn

Ik ben een Cloud Engineer met een sterke basis in elektrotechniek, machine learning en programmeren. Mijn carrière begon in computervisie, met een focus op beeldclassificatie, waarna ik overstapte naar MLOps en DataOps. Ik ben gespecialiseerd in het bouwen van MLOps-platformen, het ondersteunen van data scientists en het leveren van Kubernetes-gebaseerde oplossingen om machinelearning-workflows te stroomlijnen.

Onderwerpen

Leer meer over Spark met deze cursussen!

Cursus

Machine Learning met PySpark

4 Hr
29.3K
Bekijk detailsRight Arrow
Begin met de cursus
Meer zienRight Arrow