PySpark, de la zero Lecția 3 / 60

Ce este Spark si de ce a inlocuit Hadoop MapReduce

Lucrarea lui Matei Zaharia din 2010, executia in memorie, DAG-ul, evaluarea lazy si afirmatia 'de 100 de ori mai rapid' - ce inseamna cu adevarat si ce nu.

Am terminat lecția 2 cu MapReduce ajungând la sfârșitul vieții sale utile pe la 2014, sufocându-se cu I/O de disc și cu presupuneri arhitecturale din deceniul anterior. Astăzi privim sistemul care l-a înlocuit: Apache Spark. Până la sfârșitul acestei lecții vei ști de unde a venit Spark, ce face el că Hadoop MapReduce nu putea, ce înseamnă cu adevărat faimoasa afirmație „de 100 de ori mai rapid” și, la fel de important, ce nu este Spark, fiindcă jumătate din confuzia despre Spark din industrie vine de la oameni care cred că e o bază de date, sau un sistem de stocare, sau un scheduler, când nu e niciuna dintre acelea.

De unde a venit

Spark s-a născut în 2009 la AMPLab la UC Berkeley, un grup de cercetare axat pe algoritmi, mașini și oameni (de la algorithms, machines, people, vine A, M, P). Autorul principal a fost un doctorand pe nume Matei Zaharia, lucrând sub profesorii Scott Shenker și Ion Stoica. Zaharia lucrase la Facebook și văzuse de aproape problema învățării automate iterative din MapReduce, cercetători rulând același algoritm pe același dataset, plătind costul integral de I/O de disc la fiecare iterație. S-a apucat să repare exact asta.

Prima lansare publică a Spark a fost în 2010. Lucrarea fondatoare, „Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing”, a fost publicată la NSDI în 2012. Zaharia și colaboratorii săi au fondat Databricks în 2013, care e acum vendor-ul comercial dominant de Spark și e și, la sfârșitul lui 2026, compania care conduce efectiv cea mai mare parte din roadmap-ul Spark. Proiectul a fost donat fundației Apache Software la mijlocul lui 2013 și a absolvit într-un proiect Apache de top în februarie 2014. Spark 1.0 a apărut în mai 2014. Spark 2.0 în 2016 a introdus API-ul modern DataFrame. Spark 3.0 în 2020 a adus adaptive query execution, una dintre acele funcționalități care fac în liniște query-urile tuturor mai rapide fără ca ei să fie nevoiți să facă nimic. Spark 4.0 a aterizat în 2025. Proiectul a fost pe o cadență de versiune majoră de aproximativ doi-trei ani și a fost remarcabil de stabil în abstracțiile sale de bază.

Vei vedea toate astea creditate către „Databricks” în material de marketing și către „Apache” în material tehnic. Ambele sunt corecte. Spark e un proiect Apache; Databricks e entitatea comercială construită în jurul lui; aceeași mână de oameni stă în centrul ambelor.

Cele trei inovații

Propunerea Spark din lucrarea originală e, distilată: ia MapReduce, dar fă-l să nu fie lent. Modul în care a făcut asta s-a redus la trei alegeri arhitecturale, fiecare dintre ele adresând unul dintre punctele specifice de durere ale MapReduce din lecția 2.

Inovația unu: o abstracție de dataset în memorie. Asta e RDD, Resilient Distributed Dataset, lucrul după care se numește lucrarea lui Zaharia. Un RDD e o colecție logică de înregistrări răspândită pe cluster, exact ca un dataset intermediar MapReduce, doar că framework-ul îl poate ține în RAM între operații în loc să-l scrie în HDFS de fiecare dată. „Resilient” din nume se referă la toleranța la eșec: fiecare RDD își amintește cum a fost derivat din părintele său (lineage-ul său), așa că dacă o partiție e pierdută din cauză că un worker a murit, Spark poate recalcula doar acea partiție din lineage în loc să repornească tot job-ul. Rezultatul e că obții toleranța la eșec a MapReduce fără I/O-ul de disc. Pentru încărcăturile iterative, unde refolosești același dataset pe multe pasaje, asta e diferența între un job care durează 30 de minute și un job care durează 30 de secunde.

Nu mai scriem mult cod RDD: folosim API-ul de nivel mai înalt DataFrame, pe care îl vom întâlni în lecția 5, dar fiecare operație DataFrame se compilează tot la RDD-uri pe dedesubt. Abstracția e fundamentală și va supraviețui API-ului din vârful ei.

Inovația doi: un DAG de transformări, nu un model fix cu două stage-uri. MapReduce forța fiecare calcul în exact un map plus un reduce. Spark îți permite să înlănțui arbitrar de multe transformări într-un singur job, iar motorul construiește un DAG, un graf aciclic direcționat, reprezentând calculul complet. Filtre, map-uri, join-uri, group-by-uri, proiecții, agregate, sortări: orice combinație, în orice ordine, totul într-un singur job logic. Motorul Spark analizează DAG-ul complet, își dă seama ce operații pot fi fuzionate împreună (de exemplu, două apeluri filter consecutive pot fi îmbinate într-unul singur), unde sunt cu adevărat necesare shuffle-uri (join-uri și group-by-uri; nu map-uri și filtre) și produce un plan fizic care rulează doar shuffle-urile de care are absolut nevoie.

Asta e o chestie mult, mult mai mare decât pare. În MapReduce, un pipeline de șase pași însemna șase job-uri distincte, șase tururi complete pe disc și șase runde de overhead de pornire JVM. În Spark, un pipeline de șase pași e un singur job, cu poate două shuffle-uri la mijloc, toată munca dintre ele întâmplându-se în memorie. Scrii același query logic și rulează cu un ordin de mărime mai rapid, fără efort suplimentar din partea ta dincolo de a folosi API-ul de nivel mai înalt.

Inovația trei: evaluarea lazy. Cele mai multe operații în Spark nu fac efectiv nimic atunci când le apelezi. df.filter(...), df.select(...), df.join(...): niciuna dintre acestea nu se execută. Construiesc DAG-ul, dar nu se întâmplă nicio muncă. Doar când apelezi o acțiune, count(), collect(), write.parquet(...), show(), intră motorul în acțiune, se uită la întregul DAG pe care l-ai construit, îl optimizează și îl rulează.

Asta face posibil optimizatorul Catalyst. Catalyst e optimizatorul de query-uri al Spark; ajunge să se uite la întregul tău calcul înainte ca vreo parte din el să ruleze și să-l rearanjeze pentru performanță. Poate să împingă filtrele în jos până la sursa de date astfel încât să citești mai puține date de pe disc în primul rând. Poate reordona join-urile astfel încât dataset-urile mai mici să fie unite mai întâi. Poate recunoaște că ai nevoie doar de două coloane dintr-un fișier Parquet cu 200 de coloane și să citească doar acele două coloane. Nimic din asta nu e posibil într-un sistem evaluat eager, unde fiecare operație rulează imediat pe rezultatul celei anterioare. Evaluarea lazy e prețul de admitere pentru un optimizator inteligent, iar optimizatorul Spark e unul dintre lucrurile care îl țin competitiv față de noi veniți.

Compromisul e că codul Spark e uneori derutant de debug-at dacă ești obișnuit cu Pandas. Scrii ce arată ca zece linii de cod perfect rezonabil, niciuna dintre ele nu rulează, apelezi .show() la sfârșit și atunci toate cele zece linii se execută ca un singur batch optimizat și orice eroare în oricare dintre ele apare la linia .show() cu un stack trace care nu se potrivește chiar cu sursa. Modulul 3 al cursului ăsta e în mare parte despre confortul cu asta.

Afirmația „de 100 de ori mai rapid” și ce înseamnă cu adevărat

Dacă ai citit ceva despre Spark, ai văzut titlul: de 100 de ori mai rapid decât Hadoop MapReduce. E pe pagina principală a spark.apache.org. A fost pe fiecare slide deck Databricks pentru cea mai mare parte din anii 2010. E, în funcție de ce măsori, fie complet adevărată, fie supravândută sălbatic. Ambele lecturi merită atenție pentru că ambele apar în conversații reale.

Unde afirmația e cu adevărat adevărată. Algoritmii iterativi, învățarea automată, procesarea grafurilor, orice refolosește același dataset pe multe pasaje, rulează cu adevărat de aproximativ 100 de ori mai rapid pe Spark decât pe MapReduce, iar benchmark-urile originale Berkeley au fost oneste despre asta. Motivul e exact ce te-ai aștepta: MapReduce citește și scrie dataset-ul în HDFS la fiecare iterație; Spark îl citește o singură dată, îl ține în RAM și îl refolosește. Dacă dataset-ul tău încape în memoria clusterului și algoritmul tău face 20 de pasaje, Spark face aproximativ 1 citire de disc unde MapReduce face 20, plus economisește 20 de runde de pornire JVM. Obții o accelerare de unu-doi ordine de mărime aproape mecanic.

Pipeline-urile cu mai multe stage-uri, șase job-uri MapReduce înlănțuite cu Oozie, rulează de asemenea cu adevărat mult mai rapid pe Spark, din același motiv. Spark fuzionează cele șase stage-uri într-un singur job, le rulează în memorie între shuffle-uri și sare peste cea mai mare parte din overhead-ul de disc și orchestrare.

Unde afirmația e supravândută sălbatic. Agregările cu o singură trecere, citește un fișier mare, fă un GROUP BY, scrie rezultatul, sunt blocate de I/O brut atât pe Spark cât și pe MapReduce. Citirea a 1 TB de pe S3 durează cam la fel indiferent dacă o faci în Spark sau în Hadoop, fiindcă rețeaua și stocarea sunt blocajul, nu calculul. În acel scenariu poți vedea Spark de 1.5x până la 3x mai rapid, uneori 5x, dar nu vei vedea 100x. Oricine îți spune că query-ul lui SELECT COUNT(*) GROUP BY country a devenit de 100 de ori mai rapid prin trecerea de la Hadoop la Spark își amintește greșit, sau compară hardware din 2010 cu hardware din 2024, sau compară un job Hive prost reglat cu un job Spark bine reglat, sau pur și simplu repetă marketing-ul.

Sumarul onest: Spark e undeva între modest mai rapid și dramatic mai rapid decât Hadoop MapReduce, în funcție de încărcătura de lucru, iar la acest punct în 2026 comparația e oricum în mare parte istorică, fiindcă nimeni nu alege între Spark și MapReduce pentru un proiect nou.

Ce nu este Spark

Asta e partea pe care nu ți-o spune nimeni, și e sursa a aproximativ jumătate din conversațiile derutate despre Spark pe care le-am avut de-a lungul anilor.

Spark nu e o bază de date. Nu îți stochează datele. Nu are tabele așa cum Postgres are tabele. „Tabelul Hive” pe care îl interoghezi prin Spark SQL e metadate care arată spre fișiere în object storage; Spark însuși nu deține nimic din asta. Nu există așa ceva ca o „bază de date Spark” așa cum există o „bază de date Postgres”. Dacă-ți ștergi bucket-ul S3, tabelele tale au dispărut, iar Spark nu te poate ajuta.

Spark nu e un sistem de stocare. Nu înlocuiește HDFS, S3, GCS, ADLS, sau orice alt loc unde-ți ții datele. Aduci stocarea; Spark citește din ea și scrie în ea. Spark are connector-i pentru zeci de formate (Parquet, ORC, JSON, CSV, Avro, Delta Lake, Iceberg, JDBC, Kafka, și multe altele), dar stocarea în sine e problema altcuiva.

Spark nu e un cluster manager. Rulează pe un cluster manager. Opțiunile tale sunt YARN (cel din Hadoop, încă comun), Kubernetes (din ce în ce mai mult default-ul în 2026), Mesos (deprecated din 2020 și eliminat în Spark 3.5, menționat doar fiindcă răspunsuri vechi pe StackOverflow îl referențiază) sau propriul standalone manager built-in al Spark (ok pentru clustere mici și medii de dezvoltare). Cluster manager-ul alocă mașini și procese; Spark le folosește.

Spark este, în mod specific, un motor de calcul distribuit. Aceea e toată descrierea job-ului. Îi dai date (de undeva), îi dai un cluster (de undeva), iar el rulează calculul tău pe cluster pe date. Stocarea și orchestrarea sunt preocupări separate, intenționat. De-asta deployment-urile Spark arată ca Lego, alege-ți stratul de stocare, alege-ți cluster manager-ul, alege-ți catalogul, alege-ți formatul de tabel, și de-asta migrarea între, să zicem, Hadoop on-prem și S3 + Kubernetes cloud-native e în mare parte un exercițiu de configurare, nu o rescriere.

Peisajul competitiv, într-un paragraf

În 2026, Spark e motorul dominant open-source de calcul distribuit, punct. Există competitori și fiecare e interesant în nișa lui. Apache Flink e cu adevărat mai bun decât Spark la încărcături de streaming adevărat cu latență scăzută, iar dacă construiești un sistem de detectare a fraudei în timp real care are nevoie de latență sub-secundă, ar trebui să te uiți la el. Dask e o alternativă Python-pură care scalează codul în stil Pandas la clustere și e mai plăcut pentru munca de data science în echipe mici care nu are nevoie de suport cross-limbaj. Ray e framework-ul de calcul distribuit Python-native cel mai asociat cu încărcăturile moderne de ML și reinforcement learning. DuckDB e, în funcție de cum privești, fie o bază de date analitică pe un singur nod, fie o amenințare serioasă pentru Spark în intervalul de date mic-spre-mediu pe care l-am discutat în lecția 1. Polars face același lucru pe partea în memorie. Toate astea sunt reale, toate au locul lor, și niciuna dintre ele nu a deplasat Spark pentru ETL batch și streaming pe warehouse-uri mai mari decât o singură mașină grasă. Acela e încă terenul de acasă al Spark, și e locul unde te va ține cursul ăsta pentru următoarele 57 de lecții.

Lecția următoare: arhitectura. Driver, executors, cluster manager, task-uri, stage-uri, job-uri, toate piesele care trebuie să fie în capul tău înainte să începem să scriem cod. După aia putem în sfârșit să instalăm ceva.

Pentru lecturi suplimentare, documentația Apache Spark e referința canonică, lucrarea originală RDD e textul fondator, iar sursa Spark de pe GitHub e cu adevărat citibilă dacă vreodată vrei să știi de ce face motorul ce face.

Caută