GH GambleHub

Read Models und Projektionen

Read Model ist eine speziell entworfene Tabelle/Index/Ansicht für schnelle Lesungen für ein bestimmtes Produktszenario. Projektion - Ein Prozess, der Ereignisse/Quelländerungen in Read Model-Updates umwandelt (normalerweise idempotent upsert). In Verbindung mit CQRS ermöglicht dies die Entlastung des OLTP-Kernels und die Stabilisierung der p95/p99-Lesungen durch Steuerung der „Frische“.

Die wichtigsten Ideen:
  • Denormalisierung auf Anfrage, nicht „universelles Schema“.
  • Inkrementell und idempotent aktualisieren.
  • Eindeutig verwalten staleness und Ordnung.

1) Wann man Read Models verwendet (und wann nicht)

Geeignet für:
  • Häufiges schweres Lesen (Joins/Aggregation/Sortierung) mit zulässiger Aktualisierungsverzögerung.
  • Dashboards, Kataloge, Landings, „Top N“, persönliche Feeds, Suchlisten.
  • Lastverteilung: Write-Core ist streng, Read-Plane ist schnell und skalierbar.
Nicht geeignet:
  • Operationen, die strenge Invarianten „für jeden Datensatz“ erfordern (Geld, Einzigartigkeit). Da ist ein starker Pfad.

2) Architektonische Kontur (verbales Schema)

1. Quelle der Änderungen: Domain Events (Event Sourcing) oder CDC aus OLTP.
2. Projektionspipeline: Parser → Aggregation/Denormalisierung → idempotent upsert.
3. Read Store: DB/Index optimiert für Anfrage (RDBMS, Säulen-, Suchmaschinen).
4. API/Client: schnelles SELECT/GET mit den Attributen „as_of/freshness“.

3) Design des Lesemodells

Beginnen Sie mit der Abfrage: welche Felder, Filter, Sortierung, Pagination, Top N?
Denormalisieren: Speichern Sie bereits zusammengeführte Daten (Namen, Beträge, Status).

Schlüssel:
  • Partitionierung: nach 'tenant _ id', Datum, Region.
  • Primärschlüssel: Geschäftsschlüssel + temporäres Baquet (z. B.'(tenant_id, entity_id) 'oder' (tenant_id, bucket_minute)').
  • Indizes: durch häufige where/order by.
  • TTL/retention: für temporäre Vitrinen (z.B. 90 Tage).

4) Aktualisierungsfluss und Idempotenz

Idempotent upsert ist die Basis für die Stabilität von Projektionen.

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);
Regeln:
  • Jede Nachricht trägt eine Version/Zeit; Wir akzeptieren nur „frisch oder gleich“ (idempotency).
  • Für Aggregate (Zähler, Summen) - State speichern und kommutative Updates (oder CRDT-Ansätze) verwenden.

5) Quelle der Veränderung: Ereignisse vs CDC

Veranstaltungen (Event Sourcing): reiche Semantik, einfach, verschiedene Projektionen zu bauen; Die Entwicklung von Schemata ist wichtig.
CDC (logische Replikation): einfach an eine bestehende Datenbank anschließen; Mupping- DML→sobyty und Filterung von Rausch-Updates sind erforderlich.

Beide Optionen erfordern:
  • Versandgarantien (at-least-once) und DLQ für „giftige“ Nachrichten.
  • Reihenfolge nach Schlüssel (partition key = 'tenant _ id: entity _ id').

6) Ordnung, Kausalität und „Frische“

Die Reihenfolge nach dem Schlüssel: die Ereignisse eines Objektes sollen nacheinander kommen; Partitionierung und Versionen verwenden.
Kausalität (Sitzung/causal): Damit der Autor seine Änderungen (RYW) sieht, übergeben Sie die Wasserzeichen-Version in Abfragen.
Frische (bounded staleness): Geben Sie „as _ of “/„ X-Data-Freshness“ zurück und halten Sie den SLO (z. B. p95 ≤ 60 c).

7) Inkrementelle Aggregate und Top N

Beispiel für Minutenverkaufsbakets:
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;
Für die Top N:
  • Pflegen Sie ein Ranking-Showcase (z.B. nach 'revenue DESC') und aktualisieren Sie nur die geänderten Positionen (heap/skiplist/limited table).
  • Speichern Sie das „Fenster“ der Spitze (z. B. 100-1000 Zeilen pro Segment).

8) Such- und Geo-Projektionen

Suche (ES/Opensearch): denormalisiertes Dokument, Pipeline von Transformationen, Dokumentversion = Quellversion.
Geo: Speichern Sie' POINT/LAT, LON', vorher aggregieren Sie die Tiers/Quadrotry.

9) Multi-Tenant und Regionen

'tenant _ id' ist in Projektionsschlüsseln und Ereignissen obligatorisch.
Fairness: Begrenzen Sie throughput Projektionen per tenant (WFQ/DRR), damit „noise“ die anderen nicht bremst.
Residency: Die Projektion lebt in der gleichen Region wie der Write-Core; interregionale Vitrinen - Aggregate/Zusammenfassungen.

10) Beobachtbarkeit und SLO

Metriken:
  • 'projection _ lag _ ms' (istochnik→vitrina), 'freshness _ age _ ms' (seit dem letzten Delta).
  • throughput-Aktualisierungen, Fehlerquote, DLQ-Rate, Redrive-Erfolg.
  • Größe der Vitrinen, p95/p99 Latenz der Lesungen.
Tracing/Logs:
  • Теги: `tenant_id`, `entity_id`, `event_id`, `version`, `projection_name`, `attempt`.
  • Anmerkungen: Merge-Lösungen, Auslassungen veralteter Versionen.

11) Playbooks (Runbooks)

1. Lagwachstum: Überprüfen Sie den Konnektor/Broker, erhöhen Sie die Parteien, aktivieren Sie die Priorisierung von Schlüsselvitrinen.
2. Viele Schemafehler: Redrive einfrieren, Schemamigration durchführen (Backfill), mit neuer Mapper-Version neu starten.
3. Wiederholte DLQs: Batch reduzieren, „Schatten“ -Handler einschalten, Idempotenz verstärken.
4. Inkonsistenz der Vitrine: Rebuild der Vitrine aus der Zeitschrift/Quelle hinter dem Fenster durchführen (selektiv nach Tenant/Partition).
5. Hot Keys: Begrenzen Sie den Wettbewerb nach Schlüssel, fügen Sie lokale Warteschlangen hinzu und nehmen Sie das Gerät in ein separates Schaufenster.

12) Komplette Neuberechnung (Rebuild) und Backfill

Der Ansatz:
  • Stoppen Sie den Verbrauch (oder wechseln Sie zu einer neuen Version des Schaufensters).
  • In Paketen neu berechnen (nach Parteien/Daten/Tenanten).
  • Aktivieren Sie den zweiphasigen Switch: Zuerst füllen Sie' read __ v2', dann schalten Sie das Routing der Lesungen atomar um.

13) Entwicklung der Schaltungen (Versionierung)

'schema _ version' in Ereignissen/Dokumenten.
Projektion kann mehrere Versionen lesen, Migration „on the fly“.
Für große Veränderungen gibt es ein neues Schaufenster v2 und Kanarienverkehr.

14) Sicherheit und Zugang

Erben Sie RLS/ACL von der Quelle; Machen Sie das Schaufenster nicht breiter als die Originaldaten.
Maskieren Sie PIIs in Projektionen, die für UX/Analytics nicht erforderlich sind.
Auditierung von Redrives/Neuberechnungen/manuellen Bearbeitungen.

15) Konfigurationsvorlage

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) Typische Fehler

„Ein Schaufenster für alle Fälle“ → schwere Upgrades und schlechte p99.
Mangel an Idempotenz → Doppel/Sprünge in den Einheiten.
Dual-write direkt ins Schaufenster und OLTP → Unstimmigkeiten.
Null Frische → ein Konflikt der Erwartungen mit dem Produkt.
Rebuild ohne Zwei-Phasen-Switch → „Löcher“ in den Antworten.
Keine Partitionierung/Indizes → steigende Kosten und Latenz.

17) Schnelle Rezepte

Katalog/Suche: Dokumentationsvitrine + inkrementelles Upsert, Lag ≤ 5-15 c, Indizes für Filter.
Dashboards: Minuten-/Stundentanks, SUM/COUNT-Einheiten, Frische p95 ≤ 60 c.
Persönlicher Feed: Projektion nach Benutzer + causal/RYW für den Autor, Fallback pro Cache.
Global SaaS: regionale Schaufenster, Aggregate überregional; fairness per tenant.

18) Checkliste vor dem Verkauf

  • Die Vitrine ist auf eine bestimmte Anforderung ausgelegt; Es gibt Indizes und Parteien.
  • Änderungsquelle ausgewählt (Ereignisse/CDC); Liefergarantie und Schlüsselauftrag.
  • Idempotent upsert mit Versionen/Zeit; Schutz vor „alten“ Ereignissen.
  • Die Frische SLO ist definiert und wird in den Antworten gegeben ('as _ of/freshness').
  • DLQ und Secure Redrive sind konfiguriert; playbook auf rebuild/backfill.
  • Wettbewerbsbeschränkungen (per-key serial) und fairness per tenant.
  • Lag/Error/Latency-Metriken, Alerts auf p95/p99 und DLQ-Wachstum.
  • Versionierung von Schemata und Migrationsstrategie (v2 + Switch).
  • Zugriffs-/PII-Richtlinien werden vererbt und validiert.

Schlussfolgerung

Read Models und Projektionen sind ein technischer Beschleuniger für Lesungen: Sie zahlen einen kleinen Preis für „Frische“ und Streaming-Infrastruktur, um vorhersehbare Millisekunden zu erhalten und den Kern der Aufnahmen zu entlasten. Gestalten Sie Schaufenster auf Wunsch, machen Sie Upgrades idempotent, messen Sie den Lag und versprechen Sie explizit Frische - und Ihre APIs bleiben auch bei steigender Belastung, Daten und Geografie schnell.

Contact

Kontakt aufnehmen

Kontaktieren Sie uns bei Fragen oder Support.Wir helfen Ihnen jederzeit gerne!

Integration starten

Email ist erforderlich. Telegram oder WhatsApp – optional.

Ihr Name optional
Email optional
Betreff optional
Nachricht optional
Telegram optional
@
Wenn Sie Telegram angeben – antworten wir zusätzlich dort.
WhatsApp optional
Format: +Ländercode und Nummer (z. B. +49XXXXXXXXX).

Mit dem Klicken des Buttons stimmen Sie der Datenverarbeitung zu.