Data pipeline-lar: batch və stream
Data pipeline-lar: batch və stream
Production database sifarişi qəbul etmək üçün qurulub, "keçən rübdə hansı region-da orta səbət dəyəri düşdü?" sualına cavab vermək üçün yox. Amma biznes məhz belə suallar verir, ML model-ləri isə tarixi data ilə öyrədilir. Ona görə böyüyən hər sistemin yanında ikinci bir sistem yaranır: data-nı operational dünyadan analitik dünyaya daşıyan pipeline-lar.
Ch03-də pipes-and-filters style-ını gördük: data müstəqil stage-lərdən keçir. Bu fəsil həmin ideyanı sistem miqyasına qaldırır: data-nı nə vaxt toplu halda, nə vaxt axın kimi emal etməli, "exactly-once" vədi əslində nə deməkdir və hansı freshness-ə nə qədər ödəməyə dəyər.
OLTP və OLAP: iki fərqli dünya
OLTP (online transaction processing) çoxlu kiçik, qısa əməliyyatla işləyir: bir sifarişi oxu, bir sətri yenilə. OLAP (online analytical processing) isə az sayda, amma nəhəng sorğu verir: milyonlarla sətri scan et, qruplaşdır, aggregate et.
| OLTP | OLAP | |
|---|---|---|
| Tipik sorğu | Bir neçə sətir, key ilə | Milyonlarla sətir, scan + aggregate |
| İstifadəçi | Application | Analitik, dashboard, ML training |
| Optimallaşdırma | Latency, concurrency | Throughput |
| Saxlama | Row-oriented | Columnar |
Production database-də analitik sorğu işlətmək cazibədardır, çünki data onsuz da oradadır. Amma bir saatlıq hesabat sorğusu cache-i analitik data ilə doldurur, I/O-nu tutur və checkout-un p99 latency-si istifadəçinin heç görmədiyi səbəbdən artır. Read replica yükü ayırır, amma row-oriented storage analitika üçün yenə yanlış alətdir.
Ona görə data çıxarılır. Dump ən sadə yoldur: gecələr cədvəli və ya updated_at üzrə dəyişən hissəni export etmək. Amma o, silinən sətirləri görmür və data bir gün köhnə olur. CDC database-in transaction log-unu oxuyub hər dəyişikliyi event kimi ötürür (ch05-dəki ADR-də alternativ kimi keçmişdi). Dəqiqdir və gecikməsi saniyələrlə ölçülür, amma idarə edilməli ayrıca sistemdir.
Batch processing: bitmiş data üzərində hesablama
Batch processing sərhədi məlum olan data toplusunu, məsələn "dünənki bütün sifarişləri", götürüb nəticə hesablayır. İdeyanın klassik forması MapReduce-dur: data hissələrə bölünür, hər hissə paralel emal olunur, nəticələr key üzrə qruplaşdırılıb birləşdirilir. Spark kimi müasir alətlər eyni ideyanı yaddaşda və daha rahat API ilə həyata keçirir, zehni model isə dəyişməyib.
Klassik ETL-də data ayrıca serverdə transform olunub sonra warehouse-a yüklənir. Müasir warehouse-lar güclü və nisbətən ucuz olduğu üçün indi çox vaxt ELT edilir: xam data olduğu kimi yüklənir, transform isə warehouse-un içində SQL ilə aparılır. Üstünlüyü budur ki, xam data qalır və transform səhvini düzəltmək üçün mənbəyə qayıtmaq lazım gəlmir.
Batch job-un ən vacib xüsusiyyəti təkrarlana bilməsidir. Job yarıda yıxılacaq, səhv kodla işləyəcək, upstream data gec gələcək. Hamısının cavabı eynidir: job-u yenidən işlət. Bu yalnız job idempotent olanda təhlükəsizdir:
- Append yox, partition üzrə overwrite.
date=2026-10-30job-u həmin günün partition-ını tamamilə yenidən yazır. İki dəfə işləsə də, nəticə eynidir. Append isə hər təkrarda duplicate yaradır. - Job "indi"yə baxmır. Tarix parametr kimi verilir və mənbə data dəyişdirilmir.
- Nəticə atomik dərc olunur. Yarımçıq partition-ı heç kim oxumur.
Bu xüsusiyyətlər backfill-i ucuzlaşdırır. Hesablamada bug tapıldı və ya yeni sütun lazım oldu? Job-u son 90 günün hər partition-ı üçün yenidən işlədirsiniz. Idempotent olmayan pipeline-da isə backfill əl ilə təmizlik və dua deməkdir.
Stream processing: vaxt çətin hissədir
Stream processing sonu olmayan event axınını gəldikcə emal edir. Batch-də "dünən" aydın sərhəddir, stream-də isə data heç vaxt bitmir. Bütün çətinlik buradan başlayır.
Event time və processing time. Event time hadisənin baş verdiyi, processing time isə onun sistemə çatdığı andır. Offline qalan mobil tətbiqin event-ləri saatlarla gecikə bilər. "14:00–14:05 arası neçə klik oldu?" sualı event time üzrə hesablanmalıdır, yoxsa şəbəkə gecikməsi nəticəni dəyişir.
Windowing. Sonsuz axında aggregate hesablamaq üçün onu pəncərələrə bölmək lazımdır:
- Tumbling window: sabit, üst-üstə düşməyən intervallar, məsələn hər 5 dəqiqə.
- Sliding window: üst-üstə düşən intervallar, məsələn hər dəqiqə yenilənən "son 10 dəqiqə". Fraud aşkarlama və alert-lər üçün tipikdir.
- Session window: istifadəçinin aktivlik dövrü. Fasilə müəyyən müddəti keçəndə bağlanır, uzunluğu isə data-dan asılıdır.
Late data və watermark. 14:05 pəncərəsini nə vaxt bağlamaq olar? Bəlkə 14:03-ün event-i hələ yoldadır. Watermark stream processor-un "bu event time-dan əvvəlki event-lərin demək olar ki, hamısı gəlib" iddiasıdır və pəncərə watermark onu keçəndə bağlanır. Aqressiv watermark tez, amma natamam nəticə verir; ehtiyatlı watermark dəqiq, amma gec. Bu, freshness ilə correctness arasında açıq trade-off-dur. Watermark-dan sonra gələn event-lər ya atılır, ya ayrıca yerə yazılır, ya da nəticəni yeniləyir.
State. Pəncərə sayğacları, açıq session-lar, join üçün gözləyən event-lər state-dir. Processor yıxılanda itməməsi üçün o, mütəmadi olaraq durable storage-a checkpoint edilir. Stateless service-i yenidən başlatmaq asandır. Stateful stream processing job-u isə bərpa olunmalı, scale olunanda state yenidən paylanmalıdır. Stream-in əməliyyat qiymətinin böyük hissəsi buradadır.
Delivery semantikası və "exactly-once"
Pipeline hər mesaj üçün üç vəddən birini verə bilər:
- At-most-once: retry yoxdur, mesaj itə bilər. Bəzi metrikalar üçün qəbul ediləndir.
- At-least-once: itki yoxdur, amma retry duplicate yaradır. Ch09-da gördüyümüz kimi broker-lərin default-u budur.
- Exactly-once: hər mesajın təsiri nəticədə tam bir dəfə görünür.
Mesajı şəbəkə üzərində fiziki olaraq tam bir dəfə çatdırmaq mümkün deyil: ack itəndə göndərən mesajın çatıb-çatmadığını bilmir. Ona görə "exactly-once" əslində effectively-once deməkdir: mesaj bir neçə dəfə emal oluna bilər, amma nəticəyə bir dəfə təsir edir. Bunu iki mexanizm birlikdə verir:
- Checkpoint. Processor state-ini və input-da hansı offset-ə qədər oxuduğunu atomik saxlayır. Yıxılanda son checkpoint-dən davam edir və aradakı mesajları yenidən emal edir.
- Idempotent və ya transactional sink. Təkrar emal nəticəni dəyişmir: key üzrə upsert, partition overwrite və ya output-la offset-in eyni transaction-da yazılması.
Əsas dərs: exactly-once framework-un yox, bütün yolun xüsusiyyətidir. Processor daxilində hər şey düzgün olsa belə, sink email göndərirsə və ya idempotent olmayan API çağırırsa, duplicate yenə çıxacaq. Ch08-dəki idempotency key burada da işləyir.
Lambda və Kappa architecture
Bir vaxtlar stream processor-lara dəqiq nəticə üçün etibar edilmirdi. Lambda architecture bu boşluğu iki paralel yolla doldurdu. Kappa architecture isə soruşdu: stream processor dəqiq hesablaya bilirsə, batch layer niyə lazımdır?
Lambda architecture:
events ─┬─→ [batch layer] → batch views (dəqiq, saatlarla gecikmə) ─┐
│ ├─→ [serving layer] → sorğu
└─→ [speed layer] → real-time views (təxmini, saniyələr) ─┘
Kappa architecture:
events → [log, uzun retention] → [stream processor v1] → view v1 → sorğu
└────── replay ─→ [stream processor v2] → view v2 (hazır olanda keçid)
Kappa-da hər şey bir stream processing job-dur. Məntiq dəyişəndə job-un yeni versiyası log-u başdan oxuyur, yeni view-nu köhnəsinin yanında qurur və hazır olanda sorğular ona keçirilir.
| Lambda architecture | Kappa architecture | |
|---|---|---|
| Kod bazası | İki: eyni məntiq batch və stream-də | Bir |
| Yenidən hesablama | Batch layer təbii olaraq edir | Log-u replay etməklə |
| Əsas risk | İki implementasiya ayrışır, rəqəmlər uyğun gəlmir | Replay uzun çəkir, retention bahadır |
| Uyğun gəldiyi hal | Tarixi hesablama stream-də ifadə olunmayacaq qədər mürəkkəbdir | Məntiq stream-də ifadə olunur, tarix makul ölçüdədir |
Lambda architecture-in problemi texniki deyil, insani idi: eyni biznes qaydasını iki framework-də yazıb sinxron saxlamaq. Bu, ML-dəki training/serving skew ilə eyni xəstəlikdir və feature store da məhz onu müalicə etməyə çalışır. Kappa replay-i makul vaxtda bitirə bildiyiniz qədər kifayətdir. İllərlə yığılmış event-i hər dəyişiklikdə replay etmək realist deyilsə və ya ad-hoc analitika lazımdırsa, batch tərəf qayıdır, amma adətən ayrıca kod bazası kimi yox, warehouse-da SQL kimi.
Saxlama: warehouse, lake, lakehouse
| Data warehouse | Data lake | Lakehouse | |
|---|---|---|---|
| Nə saxlayır | Təmizlənmiş cədvəllər | Hər şey, xam fayllar kimi object storage-da | Lake faylları + üzərində cədvəl qatı |
| Schema | Yazanda | Oxuyanda | Yazanda, təkamül dəstəyi ilə |
| Güclü tərəf | Sürətli SQL, etibarlı rəqəmlər | Ucuz, çevik, ML üçün xam data | Hər ikisinin çoxu bir nüsxədə |
| Risk | Baha, xam data itir | "Data swamp": heç kim nəyin nə olduğunu bilmir | Gənc ekosistem, əməliyyat mürəkkəbliyi |
Hansı seçilsə də, ən böyük problem adətən storage deyil, data quality-dir. Upstream komanda sütunun adını dəyişir və ya amount-u sentdən dollara keçirir: pipeline səhvsiz işləyir, dashboard isə yanlış rəqəm göstərir. ML model-i üçün bu, səssiz drift deməkdir.
Cavab ch04-dəki ideyadır: interface-lər vədlərdir. Data contract producer ilə pipeline arasında belə bir vəddir: schema, məna, freshness və sahib. Schema təkamülü API-dəki kimi idarə olunur: sütun əlavə etmək təhlükəsizdir, silmək və ya mənasını dəyişmək breaking change-dir. Contract-ı CI-da yoxlamaq səhvi dashboard-dan pull request-ə çəkir.
Batch, yoxsa stream?
Stream daha müasir səslənir, amma qərar freshness tələbindən başlamalıdır və həmin tələbin özü sorğulanmalıdır:
| Freshness tələbi | Tipik istifadə | Yanaşma |
|---|---|---|
| Gün | Maliyyə hesabatı, model training | Gündəlik batch |
| Saat | Əməliyyat dashboard-ları | Saatlıq batch |
| Dəqiqələr | Stok səviyyəsi, kampaniya monitorinqi | Micro-batch və ya stream |
| Saniyələr | Fraud aşkarlama, real-time feature-lər | Stream processing |
Üç sual çox vaxt cavabı dəyişir. Bu data-ya baxıb kim dərhal hərəkət edir? Dashboard-a gündə bir dəfə baxılırsa, real-time pipeline heç kimə xidmət etmir. Gecikmə nəyə başa gəlir? Fraud-u beş dəqiqə gec görmək pul itkisidir, həftəlik hesabatın bir saat gecikməsi isə heç nədir. Səhv nəticə necə düzəldilir? Batch-də partition yenidən işlədilir, stream-də isə state, offset və replay ilə məşğul olmaq lazımdır.
Stream-in əməliyyat qiyməti realdır: 24/7 işləyən cluster, state bərpası, watermark tənzimləməsi, lag monitorinqi. Batch job yıxılanda səhər yenidən işlədilir. Stream processing job yıxılanda isə kimsə gecə oyanır. Çox vaxt düzgün cavab qarışıqdır: həqiqətən real-time olan bir-iki ehtiyac üçün stream, qalan hər şey üçün sadə və təkrarlana bilən batch.
Hazırkı sisteminiz haqqında suallar
- Production database-inizdə hesabat sorğuları işləyirmi? Ən ağırı işləyəndə checkout latency-sinə nə olur?
- Ən vacib batch job-unuzu ardıcıl iki dəfə işlətsəniz nə baş verər? Keçən ayın backfill-i neçə əl əməliyyatı tələb edər?
- "Real-time" adlandırdığınız pipeline-ların nəticəsinə kim və nə qədər tez reaksiya verir? Hansını saatlıq batch əvəz edə bilər?
- Upstream komanda bir sütunun mənasını dəyişsə, bunu ilk kim görər: CI, monitoring, yoxsa rəhbərliyin dashboard-u?