Read Models və proyeksiyalar
Read Model xüsusi bir məhsul ssenarisi altında sürətli oxumaq üçün xüsusi hazırlanmış bir cədvəl/indeks/növdür. Proyeksiya hadisələri/mənbə dəyişikliklərini Read Model yeniləmələrinə (adətən idempotent upsert) çevirən bir prosesdir. CQRS ilə birlikdə bu, OLTP nüvəsini boşaltmağa və p95/p99 oxunuşlarını sabitləşdirməyə imkan verir.
Əsas fikirlər:- «Universal sxem» deyil, sorğu altında denormallaşdırın.
- Artım və idempotent yeniləyin.
- Aydın staleness və nizam idarə.
1) Nə zaman istifadə Read Models (və nə zaman - yox)
Uyğun:- Icazə verilən yeniləmə gecikməsi ilə tez-tez ağır oxunmalar (joins/aqreqasiya/çeşidləmə).
- Daşbordlar, kataloqlar, lendinqlər, «top-N», şəxsi fidlər, axtarış siyahıları.
- Yük bölgüsü: write-nüvə - sərt, read-müstəvisi - sürətli və miqyaslı.
- «Hər bir qeyd üçün» ciddi invariantlar tələb edən əməliyyatlar (pul, unikallıq). Orada - strong path.
2) Memarlıq konturu (söz sxemi)
1. Dəyişiklik mənbəyi: ALTP-dən domen hadisələri (event sourcing) və ya CDC.
2. proyeksiya konveyer: parser → aqreqasiya/denormalizasiya → idempotent upsert.
3. Read Store: Sorğu üçün optimallaşdırılmış DB/indeks (RDBMS, sütunlar, axtarış).
4. API/müştəri: sürətli SELECT/GET, «as_of/freshness» atributları ilə.
3) Read Model dizayn
Sorğu ilə başlayın: hansı sahələr, filtrlər, çeşidləmə, paginasiya, top-N?
Denormallaşdırın: artıq birləşdirilmiş məlumatları (adlar, məbləğlər, statuslar) saxlayın.
- Partizanlaşdırma: 'tenant _ id', tarix, region.
- Primary key: biznes açarı + müvəqqəti baket (məsələn, '(tenant_id, entity_id)' və ya '(tenant_id, bucket_minute)').
- Indekslər: tez-tez where/order by.
- TTL/retenshn: müvəqqəti vitrinlər üçün (məsələn, 90 gün).
4) Yeniləmə axını və idempotentlik
Idempotent upsert - proyeksiyaların sabitliyinin əsasını təşkil edir.
Psevdo:sql
-- Projection table
CREATE TABLE read_orders (
tenant_id TEXT,
order_id UUID,
status TEXT,
total NUMERIC(12,2),
customer JSONB,
updated_at TIMESTAMP,
PRIMARY KEY (tenant_id, order_id)
);
-- Idempotent update by event
INSERT INTO read_orders(tenant_id, order_id, status, total, customer, updated_at)
VALUES (:tenant,:id,:status,:total,:customer,:ts)
ON CONFLICT (tenant_id, order_id) DO UPDATE
SET status = EXCLUDED. status,
total = EXCLUDED. total,
customer = COALESCE(EXCLUDED. customer, read_orders. customer),
updated_at = GREATEST(EXCLUDED. updated_at, read_orders. updated_at);
Qaydalar:
- Hər mesaj bir versiya/vaxt daşıyır; yalnız «təzə və ya bərabər» (idempotency) qəbul.
- Aqreqatlar üçün (sayğaclar, məbləğlər) - state saxlayın və kommutativ yeniləmələrdən (və ya CRDT yanaşmalarından) istifadə edin.
5) Dəyişiklik mənbəyi: hadisələr vs CDC
Hadisələr (event sourcing): zəngin semantika, müxtəlif proyeksiyaları qurmaq asandır; sxemlərin təkamülü vacibdir.
CDC (məntiqi replikasiya): sadəcə mövcud DB qoşulmaq; DML → hadisələrin mappinqinə və səs-küy yeniləmələrinin süzülməsinə ehtiyac duyulur.
- «Zəhərli» mesajlar üçün çatdırılma (at-least-once) və DLQ zəmanətləri.
- Açar sırası (partition key = 'tenant _ id: entity _ id').
6) Sifariş, səbəb və «təravət»
Açar qaydası: bir obyektin hadisələri ardıcıl olaraq gəlməlidir; partizan və versiyası istifadə edin.
Səbəblər (session/causal): Müəllif öz dəyişikliklərini (RYW) görsün, sorğularda watermark versiyasını verin.
Təravət (bounded staleness): 'as _ of '/' X-Data-Freshness' qaytarın və SLO saxlayın (məsələn, p95 ≤ 60 c).
7) İnkremental aqreqatlar və top-N
Dəqiqə satış baketlərinin nümunəsi:sql
CREATE TABLE read_sales_minute (
tenant_id TEXT,
bucket TIMESTAMP, -- toStartOfMinute revenue NUMERIC(14,2),
orders INT,
PRIMARY KEY (tenant_id, bucket)
);
-- Update by Event
INSERT INTO read_sales_minute(tenant_id, bucket, revenue, orders)
VALUES (:tenant,:bucket,:amount, 1)
ON CONFLICT (tenant_id, bucket) DO UPDATE
SET revenue = read_sales_minute. revenue + EXCLUDED. revenue,
orders = read_sales_minute. orders + 1;
Top N üçün:
- Sıralı vitrini (məsələn, 'revenue DESC') saxlayın və yalnız dəyişmiş mövqeləri (heap/skiplist/limited table) yeniləyin.
- Üst «pəncərə» saxlayın (məsələn, seqment başına 100-1000 sətir).
8) Axtarış və geo-proyeksiyalar
Axtarış (ES/Opensearch): denormallaşdırılmış sənəd, pipeline transformasiyalar, sənəd versiyası = mənbə versiyası.
Geo: 'POINT/LAT, LON' saxlayın, tayları/kvadrotri əvvəlcədən yığın.
9) Multi-tenant və regionlar
'tenant _ id' proyeksiyaların və hadisələrin açarlarında tələb olunur.
Fairness: throughput proyeksiyaları per tenant (WFQ/DRR) məhdudlaşdırın ki, «səs-küylü» digərlərini yavaşlatmasın.
Residency: proyeksiya write nüvəsi ilə eyni bölgədə yaşayır; regionlararası vitrinlər - aqreqatlar/hesabatlar.
10) Müşahidə və SLO
Metriklər:- 'projection _ lag _ ms' (mənbə → vitrin), 'freshness _ age _ ms' (son deltadan bəri).
- update throughput, səhv payı, DLQ-rate, redrive-success.
- Vitrin ölçüsü, p95/p99 oxunuş gecikməsi.
- Теги: `tenant_id`, `entity_id`, `event_id`, `version`, `projection_name`, `attempt`.
- Şərhlər: merge həlləri, köhnəlmiş versiyaları qaçırmaq.
11) Playbooks (runbooks)
1. Logun böyüməsi: konnektoru/brokeri yoxlayın, hissələri artırın, əsas vitrinlərin prioritetləşdirilməsini daxil edin.
2. Bir çox sxem səhvləri: redrave dondurun, sxemlərin miqrasiyasını (backfill) yerinə yetirin, mapperin yeni versiyası ilə yenidən başlayın.
3. Təkrar DLQ: batch azaltmaq, «kölgə» prosessor yandırmaq, idempotent gücləndirmək.
4. Vitrin uyğunsuzluğu: jurnaldan/mənbədən pəncərədən (tenant/partition ilə seçici olaraq) vitrin rebuild edin.
5. Qaynar açarlar: açar üzrə rəqabəti məhdudlaşdırın, yerli növbələr əlavə edin, vahid ayrı bir vitrinə çıxarın.
12) Tam hesablama (rebuild) və backfill
Yanaşma:- İstehlakı dayandırın (və ya vitrin yeni versiyası keçid).
- Paketlərlə (partiyalar/tarixlər/tenantlar üzrə) say.
- İki fazalı çeviricini işə salın: əvvəlcə 'read __ v2' doldurun, sonra oxu marşrutunu atomik olaraq dəyişdirin.
13) Sxemlərin təkamülü (versiyalaşdırma)
'schema _ version' hadisə/sənədlərdə.
Proyeksiya bir neçə versiyanı oxuya bilir, «uçuşda» miqrasiya.
Böyük dəyişikliklər üçün - yeni vitrin v2 və kanarya trafiki.
14) Təhlükəsizlik və giriş
Mənbədən RLS/ACL miras; vitrin mənbə məlumatlarından daha geniş olsun.
PII-ni UX/analitik üçün lazım olmayan proyeksiyalarda maskalayın.
Redrayvların/yenidən hesablamaların/əl düzəlişlərinin auditi.
15) Konfiqurasiya şablonu
yaml projections:
read_orders:
source: kafka. orders. events partition_key: "{tenant_id}:{order_id}"
idempotency: version_ts upsert:
table: read_orders conflict_keys: [tenant_id, order_id]
freshness_slo_ms: 60000 dlq:
topic: orders. events. dlq redrive:
batch: 500 rate_limit_per_sec: 50 read_sales_minute:
source: cdc. orders partition_key: "{tenant_id}:{bucket_minute}"
aggregate: increment retention_days: 90 limits:
per_tenant_parallelism: 4 per_key_serial: true observability:
metrics: [projection_lag_ms, dlq_rate, redrive_success, read_p95_ms]
16) Tipik səhvlər
«Bütün hallarda bir vitrin» → ağır yeniləmə və pis p99.
aqreqatlarda idempotentlik → dubl/atlama yoxdur.
Dual-write birbaşa vitrin və OLTP → fərqlər.
Sıfır təzəlik görünürlük → məhsul ilə gözləntilər toqquşması.
Rebuild iki fazalı switch → cavablarda «dəliklər» olmadan.
Heç bir partizanlaşdırma/indekslər → artan dəyər və gizli.
17) Sürətli reseptlər
Kataloq/axtarış: sənədli vitrin + incremental upsert, lag ≤ 5-15 c, filtrlər üçün indekslər.
Daşbordlar: dəqiqə/saat baketləri, 'SUM/COUNT' aqreqatları, p95 təravət ≤ 60 c.
Şəxsi lent: müəllif üçün + causal/RYW istifadəçi proyeksiyası, cache üçün fallback.
Global SaaS: regional vitrinlər, kross-regional aqreqatlar; fairness per tenant.
18) Satış öncəsi yoxlama siyahısı
- Vitrin xüsusi sorğu üçün nəzərdə tutulmuşdur; indekslər və partiyalar var.
- Dəyişiklik mənbəyi seçildi (hadisələr/CDC); çatdırılma zəmanəti və açar qaydası.
- Versiyaları/vaxtı ilə Idempotent upsert; «köhnə» hadisələrdən qorunmaq.
- SLO təravəti müəyyən və cavablarda verilir ('as _ of/freshness').
- DLQ və təhlükəsiz redrave özelleştirilmiş; rebuild/backfill playbook.
- Rəqabət məhdudiyyətləri (per-key serial) və fairness per tenant.
- Lag/səhv/latency metrikləri, p95/p99 və DLQ artımı.
- Sxemlərin versiyalaşdırılması və miqrasiya strategiyası (v2 + svitch).
- Giriş/PII siyasətləri irsi və yoxlanılmışdır.
Nəticə
Read Models və proyeksiyalar mühəndislik oxu sürətləndiricisidir: proqnozlaşdırılan millisaniyələri almaq və yazıların nüvəsini boşaltmaq üçün kiçik bir təzəlik və axın infrastrukturu üçün ödəyirsiniz. İstək üçün vitrinləri layihələndirin, yenilənmələri idempotent edin, gecikməni ölçün və açıq şəkildə təravət vəd edin - API-ləriniz yükün, məlumatların və coğrafiyanın artması ilə belə sürətli qalacaq.