Arhitectura datelor și a sistemelor, de la zero Lecția 34 / 80

Fundamentele procesării batch: lecțiile Hadoop

Ce a făcut bine MapReduce, ce a făcut prost și forma de procesare batch care a supraviețuit.

Lecția 33 s-a încheiat cu observația că warehouse-urile cloud moderne fac procesarea batch invizibilă: scrii SQL și undeva dedesubt un motor distribuit citește fișiere din object storage și produce răspunsul. Motorul invizibil nu e magie. E descendentul unui anumit articol publicat de Google în 2004, pe care o comunitate open-source l-a recuperat timp de cincisprezece ani, apoi l-a depășit, apoi l-a pensionat în liniște. Lecția asta parcurge ascensiunea și căderea Hadoop, pentru că părțile care au supraviețuit sunt încă ideile portante de sub fiecare sistem batch pe care-l vei folosi.

Motivul pentru care merită să petrecem o lecție pe o tehnologie pe care nimeni nu e entuziasmat să o pună în producție în 2026 e că tehnologia a avut dreptate în moduri pe care marketingul le-a obscurizat, și a greșit în moduri la fel de instructive. Motoarele batch moderne nu au înlocuit ideile Hadoop; i-au înlocuit implementarea.

Articolele din 2003 și 2004

În octombrie 2003, Sanjay Ghemawat, Howard Gobioff și Shun-Tak Leung au publicat „The Google File System” la SOSP. Articolul descria un filesystem distribuit care rula pe mașini ieftine de larg consum, presupunea că discurile și rețelele vor cădea în mod constant și era optimizat pentru tiparul de acces pe care îl aveau efectiv workload-urile batch ale Google: citiri secvențiale mari și append-uri mari, cu foarte puține scrieri mici aleatorii. GFS nu era un filesystem cu scop general. Era un filesystem modelat pe o anumită formă de muncă, iar forma conta mai mult decât eleganța.

În decembrie 2004, Jeffrey Dean și Sanjay Ghemawat au continuat cu „MapReduce: Simplified Data Processing on Large Clusters” la OSDI. Articolul descria un model de programare și un runtime: scrii o funcție map care procesează un record la un moment dat și o funcție reduce care agregă recordurile după o cheie, iar runtime-ul ar distribui calculul pe mii de mașini, ar gestiona căderile, ar reporni task-urile moarte și ar produce un rezultat. Perechea de articole (layer de stocare plus layer de execuție) a definit un sistem complet pentru rularea de joburi batch la scara la care Google le rula deja intern.

Doug Cutting și Mike Cafarella, lucrând la motorul de căutare Nutch, au început în 2005 o reimplementare clean-room a GFS și MapReduce în Java. Până în 2006 proiectul s-a mutat la Yahoo, a fost redenumit Hadoop și a devenit standardul open-source pentru procesarea batch distribuită. Yahoo, Facebook, LinkedIn, Twitter și majoritatea companiilor de internet care au crescut în jurul lui 2008-2012 au rulat la un moment dat pe clustere Hadoop. Până în 2012, „big data” și „Hadoop” erau aproape sinonime în presa de specialitate, iar trei distribuții comerciale (Cloudera, Hortonworks, MapR) concurau pentru piața enterprise.

Asta e arcul narativ. Conținutul tehnic de dedesubt e ce contează.

Modelul MapReduce

Un job MapReduce are trei faze. Map rulează o funcție furnizată de utilizator pe fiecare record de intrare independent, în paralel, pe multe mașini. Funcția emite zero sau mai multe perechi cheie-valoare. Shuffle e treaba runtime-ului: redistribuie toate perechile emise prin cluster așa încât toate perechile care împart o cheie să ajungă pe aceeași mașină. Reduce rulează o altă funcție furnizată de utilizator, o dată per cheie, cu toate valorile pentru acea cheie ca input, și emite output-ul final.

Exemplul clasic e numărarea de cuvinte. Funcția map citește fiecare linie a unui document și emite (word, 1) pentru fiecare cuvânt găsit. Shuffle-ul grupează toate perechile (word, 1) după cuvânt. Funcția reduce primește (word, [1, 1, 1, ...]) și emite (word, total). Tot calculul înseamnă două funcții scurte de la utilizator plus un runtime care gestionează distribuția.

Modelul e suficient de simplu să încapă pe un slide și suficient de puternic să exprime majoritatea muncii de agregare, join și filtrare. Query-urile SQL pot fi compilate în joburi MapReduce (Hive a făcut asta din 2008 încoace). Algoritmii pe grafuri pot fi exprimați ca pași MapReduce iterați. Joburile ETL se mapează natural pe el.

flowchart LR
    I[(Input split<br/>on GFS/HDFS)] --> M1[Map task 1]
    I --> M2[Map task 2]
    I --> M3[Map task 3]
    M1 --> SH[Shuffle<br/>partition by key]
    M2 --> SH
    M3 --> SH
    SH --> R1[Reduce task 1]
    SH --> R2[Reduce task 2]
    R1 --> O[(Output<br/>on GFS/HDFS)]
    R2 --> O

Ce a făcut bine MapReduce

Patru idei au supraviețuit din era MapReduce și sunt acum presupuneri portante ale fiecărui motor batch modern.

Adu codul la date, nu datele la cod. Task-urile map rulează pe aceleași mașini care țin datele de intrare, sau cât mai aproape permite topologia rețelei. Framework-ul citește layout-ul datelor, programează fiecare task pe un nod care are blocul corespunzător local, și recurge la o citire remote doar dacă nu e disponibil niciun nod local. La scara de petabyte, mutarea codului (câțiva kilobytes de JAR compilat) la date (terabytes per nod) e dramatic mai ieftină decât mutarea datelor la cod. Acest principiu, numit data locality, e modul în care fiecare sistem batch distribuit programează munca în 2026.

Toleranță la căderi prin replicare și re-execuție. Un cluster de o mie de mașini are cel puțin o mașină care cade în orice moment dat. MapReduce a presupus asta și a integrat gestiunea căderilor în runtime: datele de intrare sunt replicate pe mai multe noduri (trei replici în HDFS implicit), iar dacă un task moare, master-ul îl programează să ruleze din nou pe alt nod. Autorii joburilor nu scriu logică de retry; framework-ul reîncearcă în numele lor. Modelul funcționează doar pentru că funcțiile map și reduce sunt pure: re-rularea lor produce același output. Această constrângere e prețul toleranței ieftine la căderi și e un preț pe care fiecare framework batch încă îl plătește.

Schema-on-read. Datele aterizează pe HDFS în orice formă au sosit: linii de log, blob-uri JSON, recorduri binare custom. Schema nu e impusă de layer-ul de stocare; e interpretată de jobul care citește datele. Asta e opusul modelului warehouse pe care lecția 33 l-a contrastat cu ETL: într-o lume Hadoop datele brute sunt durabile și autoritative, iar structura e aplicată de cod. Tiparul persistă în fiecare arhitectură „lake” și „lakehouse” de azi.

Hardware de larg consum la scară. Înainte de MapReduce, scalarea workload-urilor analitice însemna cumpărarea unei mașini mai mari: un appliance Teradata, o cutie Oracle high-end, o partiție mainframe IBM. MapReduce a demonstrat că o mie de mașini ieftine, fiecare individual nesigură, pot rula mai rapid și mai ieftin decât una scumpă, dacă scrii software-ul presupunând că pică. Fiecare sistem de date la scară cloud e construit pe aceeași presupunere acum.

Ce a făcut prost MapReduce

Trei slăbiciuni, în ordine crescătoare a gravității.

State intermediar greu pe disc. Între faza map și faza reduce, MapReduce scria tot output-ul de shuffle pe disc. Fiecare task map își scria output-ul pe discul local, serviciul de shuffle îl citea înapoi prin rețea, iar task-ul reduce își scria rezultatele pe HDFS. Pentru un job cu un singur map și reduce, asta era acceptabil. Pentru un algoritm iterativ (training de machine learning, algoritmi pe grafuri precum PageRank, orice care face buclă peste aceleași date de mai multe ori), fiecare iterație era un drum dus-întors proaspăt prin disc. Spark avea să arate mai târziu că aceleași joburi iterative pot rula de o sută de ori mai rapid păstrând state-ul intermediar in-memory.

API-uri verbose, greu de debugat. Scrierea unui job Hadoop în Java însemna scrierea a două clase (un Mapper și un Reducer), conectarea lor într-o clasă driver, configurarea serializării și submiterea JAR-ului. Echivalentul SQL al unui select count(*) from t group by x era șaizeci de linii de boilerplate. Hive și Pig au împachetat asta în limbaje de nivel mai înalt, dar API-ul de bază era suficient de dureros încât „MapReduce expertise” a fost un titlu de job timp de câțiva ani. Debugarea unui job care a căzut pe mașina 487 din 1000 cu un stack trace care implica serializare Java era propriul ei tip special de suferință.

Complexitate operațională. Ecosistemul Hadoop a crescut exploziv între 2008 și 2014, iar rezultatul a fost un stack care necesita o mică echipă să-l opereze. Un cluster tipic rula HDFS pentru stocare, MapReduce pentru batch, YARN pentru programarea resurselor, Hive pentru SQL, HBase pentru citiri cu latență mică, ZooKeeper pentru coordonare, Oozie pentru programarea joburilor, Sqoop pentru ingest relațional, Flume pentru ingest de loguri, Kafka pentru streaming (în cele din urmă), Ranger pentru control de acces și Kerberos pentru autentificare. Fiecare componentă avea propria configurare, propriile moduri de cădere, propriile metrici, proprii daemoni de menținut în viață. Distribuțiile comerciale Hadoop făceau bani angajând oamenii care puteau efectiv să țină toate astea funcționale.

Combinația celor trei slăbiciuni e motivul pentru care „Hadoop” ca brand s-a pensionat efectiv. Cloudera și Hortonworks au fuzionat în 2019 pentru a consolida o piață care se micșora, apoi s-au privatizat în 2021. MapR și-a vândut IP-ul către HPE în 2019. Furnizorii de cloud au oferit object storage gestionat care era mai ieftin și mai ușor decât rularea HDFS, Spark gestionat care era mai rapid decât MapReduce și warehouse-uri gestionate care ascundeau cu totul cluster-ul.

Ce a supraviețuit

Implementarea s-a stins. Ideile, nu.

Programare distribuită și conștientizare a localității datelor. Fiecare motor batch modern, de la Spark la Trino la runtime-ul Dremel intern al BigQuery, programează task-uri cu conștientizare a locului unde trăiesc fizic datele. Scheduler-ul Hadoop YARN nu mai e; principiul pe care-l impunea e în extensiile de scheduling Kubernetes, în task planner-ul Spark, în fiecare motor de query serverless.

Batch tolerant la căderi ca primitivă. Presupunerea că orice task poate muri și framework-ul îl va re-rula e integrată în Spark, în Beam, în modul batch al Flink, în fiecare warehouse batch. Costul (puritate funcțională) și beneficiul (fiabilitate ieftină la scară) au trecut intacte.

Formate de stocare columnar. Apache Parquet (originat la Twitter și Cloudera, 2013) și Apache ORC (Hortonworks, 2013) au apărut din comunitatea Hadoop ca formate columnar pe disc proiectate pentru tiparele de acces analitic pe care le popularizase MapReduce. Sunt acum formatul de stocare implicit pentru date analitice în afara warehouse-ului, inclusiv pentru fiecare format de tabel lakehouse modern care construiește pe ele (Delta, Iceberg, Hudi, toate în lecția 37). HDFS s-a stins; formatele de fișier pe care le-a incubat sunt peste tot.

Object storage ca data lake. HDFS era un filesystem distribuit pe discuri atașate la cluster. Odată ce Amazon S3 (2006), Google Cloud Storage (2010) și Azure Blob Storage (2010) au oferit stocare durabilă, ieftină, la scară de internet, cu un API suficient de similar, cazul rulării propriului cluster HDFS s-a prăbușit. „Data lake-ul” din 2026 e un bucket pe S3 sau GCS sau Azure Blob, cu fișiere Parquet în ierarhii de foldere, interogate prin Spark, Trino, DuckDB sau motorul warehouse. Forma e exact cum arăta HDFS plus Hive în 2014, iar costul operațional e un singur connection string în loc de o echipă de șase persoane.

MapReduce ca primitivă în alte motoare. Modelul de execuție RDD al Spark e o generalizare a MapReduce: task-urile sunt programate pe partiții, shuffle-urile redistribuie după cheie, toleranța la căderi e prin re-execuție. Utilizatorul nu mai scrie map și reduce direct (scrie operații DataFrame sau SQL), dar runtime-ul de dedesubt face ce făcea MapReduce, cu optimizare mai bună și state intermediar in-memory.

Forma batch-ului modern

Trăgând împreună ideile care au supraviețuit, arhitectura batch modernă are aceeași formă pe care o avea MapReduce, cu părți înlocuite.

Layer-ul de stocare e object storage (S3, GCS, Azure Blob), care ține fișiere imutabile într-un format columnar (Parquet, uneori ORC), adesea organizate de un format de tabel deschis (Delta, Iceberg, Hudi) care adaugă tranzacții și versionare deasupra.

Layer-ul de calcul e decuplat de stocare și scalează independent. Spark, Trino, DuckDB și motoarele proprietare din interiorul warehouse-urilor cloud citesc toate aceleași fișiere. Poți rula mai multe motoare de calcul împotriva aceluiași lake în același timp, ceea ce ar fi fost un coșmar de configurare în era Hadoop.

Modelul de execuție e încă programare distribuită de task-uri cu data locality, toleranță la căderi prin re-execuție și o fază de shuffle între etape. API-ul orientat spre utilizator e SQL sau DataFrame-uri, nu funcții brute map și reduce, dar runtime-ul de dedesubt face aceeași muncă pe care a descris-o articolul din 2004.

Modelul operațional e dramatic mai simplu. Nu există cluster de menținut în viață între joburi în cazul warehouse-ului; există un singur operator Kubernetes și un singur Helm chart în cazul Spark-on-Kubernetes. core-site.xml de o mie de linii a dispărut.

Unde duce asta

Motorul batch dominant cu scop general al erei moderne, cel care a făcut puntea de la lumea Hadoop la cea cloud-native, e Apache Spark. Spark e ce caută majoritatea echipelor când au nevoie de procesare batch care nu se potrivește într-un query SQL de warehouse, și e motorul de sub Databricks, cea mai mare platformă comercială construită pe lecțiile MapReduce. Lecția 35 acoperă Spark, restul stack-ului batch modern din 2026 și arborele de decizie pentru când fiecare unealtă își câștigă locul.

Citări și lecturi suplimentare

  • Sanjay Ghemawat, Howard Gobioff, Shun-Tak Leung, “The Google File System”, SOSP 2003, https://research.google/pubs/the-google-file-system/ (consultat 2026-05-01).
  • Jeffrey Dean, Sanjay Ghemawat, “MapReduce: Simplified Data Processing on Large Clusters”, OSDI 2004, https://research.google/pubs/mapreduce-simplified-data-processing-on-large-clusters/ (consultat 2026-05-01). Articolul original. Scurt și clar; merită citit chiar și azi.
  • Apache Hadoop documentation, https://hadoop.apache.org/docs/stable/ (consultat 2026-05-01).
  • Apache Parquet documentation, https://parquet.apache.org/docs/ (consultat 2026-05-01).
  • “Hadoop: The Definitive Guide” (Tom White, O’Reilly, ediția a 4-a, 2015). Referința standard pentru ecosistemul Hadoop la apogeul său; utilă pentru context istoric chiar dacă nu plănuiești să rulezi vreodată un cluster.
  • “Designing Data-Intensive Applications” (Martin Kleppmann, O’Reilly, 2017), capitolul 10. Cel mai curat rezumat modern al modelului MapReduce și al limitărilor sale.
Caută