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

De ce streaming: date mărginite vs nemărginite

Schimbarea conceptuală de la batch la streaming. De ce 'stream' este doar 'batch cu loturi foarte mici' la limită, și de ce acea limită schimbă designul.

Modulul 5 s-a încheiat cu Netflix rulând batch pe petabyți. Datele stăteau nemișcate pe S3, joburile le citeau, joburile le scriau înapoi, iar a doua zi dimineață dashboard-urile erau actualizate. Modulul 6 începe acolo unde acea imagine cedează. Datele nu stau nemișcate. Jobul nu se termină. Dashboard-ul vrea să fie corect chiar acum, nu mâine dimineață. Trecerea de la batch la streaming este cea mai consecventă decizie de arhitectură de date pe care o iau majoritatea echipelor după alegerea bazei de date, iar mișcarea conceptuală de dedesubt este suficient de mică încât să încapă într-o singură propoziție: datele nu mai sunt mărginite.

Această lecție este deschiderea conceptuală. Încă nimic Kafka, nimic Flink, nimic watermarks. Doar reformularea și compromisul, pentru că restul modulului are mai mult sens odată ce reformularea e la locul ei.

Date mărginite versus nemărginite

Cea mai curată formulare vine de la Tyler Akidau, Slava Chernyak și Reuven Lax în „Streaming Systems” (O’Reilly, 2018). Ei argumentează că vocabularul industriei a fost confuz ani de zile pentru că amesteca două întrebări ortogonale: ce formă au datele și ce abordare de procesare aplici asupra lor.

Datele mărginite sunt un set de date fix, cu început cunoscut și sfârșit cunoscut. Tranzacțiile săptămânii trecute. Log-urile lunii trecute. CSV-ul cu cinci milioane de rânduri pe care echipa de finanțe tocmai l-a exportat. Poți să-i numești dimensiunea, să-i numeri rândurile, să-i listezi fișierele. Datele mărginite au o abordare naturală de procesare (citește-le, procesează-le, scrie un rezultat, gata) și acea abordare este batch.

Datele nemărginite sunt un flux care nu se termină. Page views. Citiri de senzori. Click-uri de utilizatori. Tranzacții cu cardul. Evenimente de cont bancar. Topic-uri Kafka. Setul de date are un început (ziua în care ai început să înregistrezi) și niciun sfârșit (continui să înregistrezi). Nu poți să-i numești dimensiunea în vreun mod definitiv: până termini de numărat, numărul deja e greșit. Datele nemărginite au o abordare naturală de procesare (procesezi înregistrările pe măsură ce sosesc și nu te oprești niciodată) și acea abordare este streaming.

Reformularea contează pentru că amestecul istoric a făcut echipele să creadă că batch și streaming sunt două lumi diferite. Nu sunt. Mărginit versus nemărginit este o proprietate a datelor. Batch versus streaming este o proprietate a procesării. Poți aplica oricare dintre abordări oricărui tip de date, iar cele patru combinații există toate în producție.

Batch peste date mărginite este implicit pentru munca analitică: ETL nocturn, rapoarte săptămânale, pregătirea datelor de antrenament. Streaming peste date nemărginite este implicit pentru munca în timp real: dashboard-uri, alerte, detecție de fraudă. Batch peste date nemărginite este ce faci când iei un snapshot dintr-un stream și-l procesezi cu unelte batch (orice pipeline „încarcă evenimentele de ieri în warehouse și rulează SQL” face asta). Streaming peste date mărginite este mai rar, dar există: rerularea unui log finit de evenimente istorice printr-un engine de streaming, ceea ce este ce arată backfilling-ul în arhitectura Kappa.

Odată ce separi cele două întrebări, deciziile de arhitectură devin mai curate. Forma datelor este un fapt. Abordarea de procesare este o alegere, făcută pe baza cerințelor de latență, a potrivirii cu uneltele și a ceea ce poate opera echipa ta.

De ce contează streaming-ul în 2026

Tehnologia este viabilă de un deceniu; cererea continuă să crească. Patru presiuni împing echipele spre streaming.

Latență mai mică. Un batch zilnic reîmprospătează dashboard-ul la 6 dimineața. Un batch orar îl reîmprospătează de șaizeci de ori pe zi. Un pipeline de streaming îl reîmprospătează la fiecare câteva secunde. Pentru directori, „suficient de bun” însemna mâine dimineață. Pentru echipele de operațiuni, „suficient de bun” însemna acum o oră. Pentru sistemele de detecție a fraudei, „suficient de bun” înseamnă chiar acum, înainte ca tranzacția să se deconteze. Pragul de latență acceptabilă a scăzut timp de cincisprezece ani, iar streaming-ul e ce alegi când batch-ul nu mai poate să-l atingă.

Decizii în timp real. Unele decizii nu pot aștepta. O tranzacție cu cardul de credit trebuie aprobată sau respinsă în mai puțin de o secundă. O recomandare trebuie să se randeze odată cu pagina. Un semnal de tranzacționare trebuie să se declanșeze înainte ca prețul să se miște. Aceste workload-uri nu pot fi exprimate ca batch peste datele de ieri; necesită procesarea evenimentului pe măsură ce sosește.

Metrici operaționale. A ști starea propriului sistem este în sine o problemă de streaming. Histograme de latență API, rata de erori per endpoint, adâncimea cozii, saturarea conexiunilor de bază de date. Stack-urile moderne de observabilitate (Prometheus, Datadog, OpenTelemetry) sunt sisteme de streaming, chiar și când nimeni din echipă nu se gândește la ele așa.

Arhitectură event-driven. Un sistem de microservicii comunică prin evenimente. Serviciul de comenzi scrie un eveniment OrderPlaced; serviciul de inventar îl citește și decrementează stocul; serviciul de email trimite o confirmare; serviciul de analitice actualizează dashboard-ul. Fiecare consumator este propriul său pipeline de streaming peste aceeași sursă, iar coloana de integrare care le leagă este un sistem de streaming. Modulul 7 acoperă pattern-urile event-driven în profunzime.

Costul: streaming-ul este mai greu

Reformularea face streaming-ul să sune ca un upgrade gratuit. Nu este. Trei probleme structurale devin mai grele când treci de la date mărginite la nemărginite.

Managementul stării este mai greu. Un job batch își citește input-ul, calculează, scrie output, și iese. Starea trăiește în memorie pentru durata jobului, apoi dispare. Un job de streaming rulează la nesfârșit, iar orice agregare pe care o calculează (un counter per utilizator, o medie pe fereastră, un join în derulare) trebuie reținută peste reporniri, peste eșecuri de mașini, peste rebalansări de cluster. Lecția 43 acoperă cum stochează engine-urile majore această stare; lecția 45 acoperă ce înseamnă „exactly-once” într-un sistem a cărui stare trebuie să supraviețuiască eșecului.

Semantica timpului este mai grea. Un job batch partiționează după dată pentru că data e în date. Un job de streaming trebuie să se descurce cu două timpuri diferite simultan: timpul în care s-a întâmplat evenimentul (event time) și timpul în care sistemul l-a procesat (processing time). Nu sunt egale, pentru că evenimentele sosesc în întârziere, în afara ordinii sau după ce rețeaua le-a înghițit cincisprezece secunde. Lecția 44 acoperă watermarks, mecanismul folosit de engine-urile de streaming ca să raționeze despre event time fără să aștepte la nesfârșit întârziații.

Recuperarea după eșec este mai grea. Un job batch care eșuează la jumătate poate fi rerulat de la început. Un job de streaming care eșuează la jumătate emite output de ore întregi; „rerulează de la început” înseamnă reemitere, ceea ce e greșit dacă downstream-ul nu este idempotent. Lecția 38 a acoperit batch-ul idempotent; versiunea de streaming este structural similară, dar mai necruțătoare pentru că suprafața de eșec este continuă.

Citirea onestă: streaming-ul este răspunsul corect când latența sau volumul justifică, și răspunsul greșit când nici una, nici alta nu o face. A apela la streaming pentru că sună modern este un mod de a tripla complexitatea operațională a unui workload care mergea bine ca un cron job.

Vederea de continuum

Despărțirea dintre batch și streaming se simte categorică când o descrii pe slide-uri. În practică e un continuum, iar poziția pe continuum este setată de dimensiunea ferestrei pe care o procesezi.

Un batch zilnic este un batch cu o fereastră de o zi. Jobul rulează o dată la 2 noaptea, citește ultima zi de date și produce un rezultat. Un batch orar este un batch cu o fereastră de o oră. Un microbatch la fiecare minut este aceeași formă cu o fereastră de șaizeci de secunde. Un microbatch la fiecare secundă este aceeași formă, doar mai rapid. La un anumit punct nu mai îi spui batch și începi să-i spui streaming, dar linia e neclară. Spark Structured Streaming, pe care îl acoperă lecția 43, este explicit un sistem microbatch: procesează înregistrările în loturi mici, de obicei la fiecare câteva sute de milisecunde, și prezintă un API de streaming deasupra unui engine batch. Flink, pe de altă parte, procesează înregistrările una câte una pe măsură ce sosesc, fără batching, și este descris ca un engine „true streaming”. Distincția contează pentru latență la scara milisecundelor și pentru forma modelului de stare. Nu contează pentru întrebarea conceptuală despre dacă procesezi date mărginite sau nemărginite.

Vederea de continuum explică de ce echipele pot migra gradual. Un batch nocturn devine un batch orar când business-ul cere date mai proaspete. Batch-ul orar devine un microbatch de cincisprezece minute când o oră e prea lent. Microbatch-ul devine un pipeline de streaming când cincisprezece minute e prea lent. Fiecare pas scurtează fereastra; arhitectura își schimbă forma gradual, nu într-un singur salt.

flowchart LR
    subgraph Bounded[Bounded data]
      B1[Last week of transactions]
      B2[Monthly export]
      B3[Historical archive]
    end
    subgraph Unbounded[Unbounded data]
      U1[Page views]
      U2[Sensor readings]
      U3[Kafka topic]
      U4[CDC log]
    end
    Bounded -->|natural fit| Batch[Batch processing]
    Unbounded -->|natural fit| Stream[Stream processing]
    Bounded -.->|possible| Stream
    Unbounded -.->|via snapshot| Batch

Diagram to create: a polished version showing two columns of data sources (bounded on the left, unbounded on the right) with arrows leading down to the two processing approaches. Solid arrows for natural fit, dashed for the cross-combinations. The point is that the shape of the data and the choice of processing are independent, and the cross-combinations both exist in production.

Cele două stiluri de arhitectură

Când echipele construiesc pipeline-uri de streaming, două arhitecturi recurente apar în literatură. Merită numite acum pentru că restul Modulului 6 va continua să se refere la ele.

Arhitectura Lambda, denumită de Nathan Marz în jurul lui 2011 și dominantă din 2014 până în 2018, rulează două pipeline-uri în paralel: un pipeline batch care produce rezultate exacte din setul de date istoric complet, și un pipeline de streaming care produce rezultate aproximative cu latență mică. Rezultatele query-urilor sunt îmbinate la momentul citirii: vederea batch este corectă dar veche, vederea streaming este proaspătă dar aproximativă, iar aplicația le combină. Lambda a fost un răspuns pragmatic la limitările engine-urilor de streaming la începutul anilor 2010, când „exactly-once” era un subiect de cercetare iar „streaming cu stare la scară” era dureros. Costul era rularea și mentenanța a două pipeline-uri paralele, cu două implementări ale fiecărei transformări, două seturi de bug-uri, două seturi de preocupări operaționale.

Arhitectura Kappa, denumită de Jay Kreps în 2014, rulează un singur pipeline de streaming. Batch-ul nu este un sistem separat; batch-ul este un pipeline de streaming care rerulează un segment lung mărginit din același log. Dacă trebuie să recalculezi istoria, îndrepți un consumer nou la offset zero al topic-ului Kafka și-l lași să ruleze. Engine-ul de streaming gestionează atât coada live, cât și backfill-ul istoric cu același cod. Kappa a devenit viabilă pe măsură ce engine-urile de streaming (în special Flink) au dezvoltat semantici exactly-once puternice, stare durabilă și mecanisme de savepoint. Până în 2020 era arhitectura de la care porneau majoritatea sistemelor de streaming noi, iar Lambda era o alegere legacy, nu un default. Lecția 46 acoperă compromisurile dintre cele două în detaliu.

Motivul pentru care le introducem acum este că alegerea afectează la ce tooling apelezi. Lambda are nevoie de un engine batch și de un engine de streaming, lipite printr-un strat de servire. Kappa are nevoie de un engine de streaming puternic și de un log durabil și rerulabil. Modulul 6 își petrece cea mai mare parte a timpului pe forma Kappa pentru că asta construiesc majoritatea echipelor în 2026, dar moștenirea Lambda este peste tot în codul legacy.

Ce acoperă Modulul 6

Următoarele șapte lecții parcurg stack-ul modern de streaming. Lecția 42 acoperă Kafka, log-ul dominant și substratul pe care orice altă componentă îl citește și-l scrie. Lecția 43 compară cele trei engine-uri de stream processing care contează în 2026: Flink, Kafka Streams, Spark Structured Streaming. Lecția 44 acoperă event time, watermarks și primitivele de windowing, cea mai dens conceptuală lecție din modul, pentru că semantica timpului este locul unde streaming-ul nu mai arată ca un batch rapid. Lecția 45 acoperă procesarea exactly-once: ce înseamnă termenul de fapt și ce garanții poți cumpăra. Lecția 46 acoperă Lambda versus Kappa în profunzime. Lecția 47 acoperă Change Data Capture, pattern-ul care transformă log-ul de tranzacții al unei baze de date într-un topic Kafka. Lecția 48 închide modulul cu studiul de caz Uber pe streaming.

Firul roșu prin toate este reformularea din această lecție. Datele mărginite au o formă naturală, datele nemărginite au alta, iar următoarele șapte lecții sunt setul de unelte pentru cazul nemărginit. Setul de unelte e real; costul e real; pârghia e enormă atunci când ai nevoie de el.

Citări și lecturi suplimentare

  • Tyler Akidau, Slava Chernyak, Reuven Lax, „Streaming Systems” (O’Reilly, 2018). Cartea de referință pentru modelul conceptual folosit în acest modul. Reformularea mărginit/nemărginit, distincția event-time/processing-time și modelul de windowing vin toate din această carte și din lucrările Google anterioare din spatele ei.
  • Tyler Akidau, „The world beyond batch: Streaming 101” și „Streaming 102”, O’Reilly Radar, 2015 și 2016. Cele două eseuri care au introdus formularea către comunitatea mai largă înainte ca cartea să apară. Încă merită citite; mișcările conceptuale sunt aceleași.
  • Nathan Marz, „Big Data: Principles and Best Practices of Scalable Real-Time Data Systems” (Manning, 2015). Cartea originală despre arhitectura Lambda.
  • Jay Kreps, „Questioning the Lambda Architecture”, O’Reilly Radar, 2014, https://www.oreilly.com/radar/questioning-the-lambda-architecture/ (consultat 2026-05-01). Eseul care a numit Kappa și a argumentat împotriva rulării a două pipeline-uri.
  • „Designing Data-Intensive Applications” (Martin Kleppmann, O’Reilly, 2017), capitolul 11. Referința standard pentru stream processing într-un context de sisteme, cu o formulare mai conservatoare decât cartea Akidau.
Caută