Citiți modele și proiecții
Read Model este un tabel special conceput/index/vizualizare pentru citiri rapide pentru un anumit scenariu de produs. Proiecția este un proces care convertește evenimentele/modificările sursă la actualizările Read Model (de obicei upsert idempotent). În combinație cu CQRS, acest lucru vă permite să descărcați nucleul OLTP și să stabilizați citirile p95/p99, controlând „prospețimea”.
Idei principale:- Denormalizați la cerere, nu o „schemă universală”.
- Actualizați incremental și idempotent.
- Gestionați în mod explicit staleness și ordine.
1) Când să utilizați Read Models (și când nu)
Fit:- Citiri frecvente (alăturări/agregări/sortimente) cu latență acceptabilă de actualizare.
- Tablouri de bord, cataloage, pagini de destinație, „top-N”, feed-uri personale, liste de căutare.
- Partajarea sarcinii: scrieți-core - strict, citiți-plan - rapid și scalabil.
- Operațiuni care necesită invarianți stricți „pe intrare” (bani, unicitate). Există o cale puternică.
2) Contur arhitectural (schema verbală)
1. Sursa modificărilor: evenimente ale domeniului (evenimente de aprovizionare) sau CDC de la OLTP.
2. Conductă de proiecție: parser → agregare/denormalizare → upsert idempotent.
3. Read Store: baza de date/index optimizat pentru interogare (RDBMS, coloană, căutare).
4. API/client: SELECT/GET rapid, cu atribute „as_of/freshness”.
3) Citiți designul modelului
Începeți cu o interogare: ce câmpuri, filtre, sortare, paginare, top N?
Denormalize: Stocați datele deja îmbinate (nume, sume, stări).
- Partiționare: prin 'chiriaș _ id', dată, regiune.
- Cheie principală: cheie de afaceri + găleată de timp (de exemplu, „(tenant_id, entity_id)” sau „(tenant_id, bucket_minute)”).
- Indici: prin frecvente unde/ordine de.
- TTL/retenție: pentru cazurile de afișare temporară (ex. 90 de zile).
4) Actualizați fluxul și idempotența
Idempotent upsert este baza stabilității proiecției.
Pseudo: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);
Reguli:
- Fiecare mesaj poartă o versiune/timp; acceptă doar „proaspăt sau egal” (idempotență).
- Pentru agregate (contoare, sume) - stocați starea și utilizați actualizări comutative (sau abordări CRDT).
5) Sursa de schimbare: Evenimente vs CDC
Evenimente (evenimente de aprovizionare): semantică bogată, este ușor de construit proiecții diferite; evoluția circuitelor este importantă.
CDC (replicare logică): pur și simplu conectați-vă la o bază de date existentă; va fi necesară filtrarea DML→sobyty a cartografierii și a actualizării zgomotului.
- Garanții de livrare (cel puțin o dată) și DLQ pentru mesaje „otrăvitoare”.
- Ordine după cheie (cheie partiție = 'chiriaș _ id: entity _ id').
6) Ordine, cauzalitate și „prospețime”
Ordine după cheie: evenimentele unui obiect trebuie să vină secvențial; utilizați partiționarea și versiunile.
Sesiune/cauzalitate: Pentru ca autorul să vadă modificările lor (RYW), treceți versiunile filigranului în interogări.
Staleness mărginită: Return' as _ of '/' X-Data-Freshness 'şi ţineţi apăsat SLO (de ex. p95 ≤ 60c).
7) agregate incrementale și de top N
Exemplu de găleți de vânzări minuscule: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;
Pentru Top N:
- Mențineți o vitrină clasată (de exemplu, prin „DESC de venituri”) și actualizați numai pozițiile schimbate (heap/skiplist/tabel limitat).
- Stocați „fereastra” din partea de sus (de exemplu, 100-1000 linii pe segment).
8) Căutare și geo-proiecție
Căutare (ES/Opensearch): document denormalizat, transformări de conducte, versiune document = versiune sursă.
Geo: stochează „POINT/LAT, LON”, plăci/cuaduri pre-agregate.
9) Multi-chiriaș și regiuni
'tenant _ id' este necesar în cheile de proiecție și evenimente.
Corectitudine: limitați debitul de proiecții per chiriaș (WFQ/DRR), astfel încât „zgomotul” să nu încetinească restul.
Rezidență: proiecția trăiește în aceeași regiune cu nucleul de scriere; vitrine interregionale - agregate/rezumate.
10) Observabilitate și SLO
Măsurători:- 'projection _ lag _ ms' (istochnik→vitrina),' prospeţime _ age _ ms' (de la ultima deltă).
- transfer de actualizări, rata de eroare, rata DLQ, redrive-succes.
- Dimensiunea ferestrei, p95/p99 latență lectură.
- Теги: 'tenant _ id',' entity _ id', 'event _ id',' version ',' projection _ name ',' încercare '.
- Adnotări: soluții de îmbinare, omiterea versiunilor învechite.
11) Cărți de joc (runbooks)
1. Creșterea decalajului: verificați conectorul/brokerul, creșteți părțile, includeți prioritizarea vitrinelor cheie.
2. Multe erori de schemă: înghețați redrive, migrați scheme (backfill), reporniți cu o nouă versiune a mapper-ului.
3. DLQ repetat: reduceți lotul, activați manipulatorul „shadow”, creșteți idempotența.
4. Inconsistența ferestrei: reconstruiți ferestrele din jurnal/sursă pe fereastră (chiriaș/partiție selectivă).
5. Tastele fierbinți: limitați concurența prin cheie, adăugați cozi locale, puneți unitatea într-o vitrină separată.
12) Renumărarea completă (reconstrui) și rambursare
Abordare:- Opriți consumul (sau treceți la o nouă versiune a casetei de prezentare).
- Recalculați în loturi (după lot/dată/chiriaș).
- Activați comutatorul în două faze: mai întâi completați „citiți __ v2”, apoi comutați atomic rutarea citirii.
13) Evoluția circuitelor (versioning)
'schema _ version' in evenimente/documente.
Proiecția poate citi mai multe versiuni, migrarea pe zbor.
Pentru schimbări majore - o nouă vitrină v2 și trafic canar.
14) Securitate și acces
Moșteniți RLS/ACL de la sursă; nu face vitrina mai largă în acces decât datele originale.
Masca PII în proiecții care nu sunt necesare pentru UX/analytics.
Auditul redrives/renumarari/editari manuale.
15) Șablon de configurare
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) Erori tipice
„O casetă de prezentare pentru toate cazurile” → actualizări grele și p99 rău.
Lipsa idempotenței → duplicate/salturi în agregate.
Dual-scrie direct la caseta de prezentare și OLTP → discrepanțe.
Vizibilitatea zero a prospețimii → așteptările contradictorii cu produsul.
Reconstrui fără un comutator în două faze → „găuri” în răspunsuri.
Fără partiționare/indici → creștere a costurilor și a latenței.
17) Rețete rapide
Catalog/căutare: prezentare document + upsert incremental, lag ≤ 5-15 s, indici pentru filtre.
Tablouri de bord: rezervoare minut/oră, unități 'SUM/COUNT', prospețime p95 ≤ 60 s.
Bandă personală: proiecție după utilizator + cauzal/RYW pentru autor, rezervă la memoria cache.
Global SaaS: vitrine regionale, agregate transregional; corectitudine per chiriaș.
18) Lista de verificare pre-vânzare
- Caseta de prezentare este proiectată pentru o anumită cerere; există indici și partide.
- Sursa de schimbare selectată (evenimente/CDC); garanții de livrare și comandă cheie.
- Upsert idempotent cu versiuni/timp; protecție împotriva evenimentelor „vechi”.
- Prospețimea SLO este definită și răspunde („as _ of/prospețime”).
- DLQ și Secure Release configurate; playbook pe reconstrui/backfill.
- Per-cheie serial și corectitudine per chiriaș.
- Măsurători de lag/eroare/latență, alerte p95/p99 și creștere DLQ.
- Strategia de versionare și migrare a circuitului (v2 + switch).
- Politicile de acces/PII sunt moștenite și validate.
Concluzie
Citește Modele și proiecții sunt un accelerator de inginerie de citește: plătiți un preț mic pentru „prospețime” și infrastructura de streaming pentru a obține milisecunde previzibile și descărcați nucleul de înregistrări. Design storefronturi pentru a se potrivi cererii dvs., face actualizări idempotent, măsura lag și promite în mod clar prospețime - și API-urile vor rămâne rapide, chiar și cu creșterea sarcinii, date și geografie.