閱讀模型和投影
Read Model是一種專門設計的表格/索引/視圖,用於針對特定產品腳本進行快速閱讀。投影是將事件/源更改轉換為Read Model更新(通常為idempotent upsert)的過程。與CQRS結合,這允許卸載OLTP內核並穩定p95/p99讀數,從而控制「新鮮度」。
主要想法:- 根據請求而不是「通用方案」進行非規範化。
- 增量和偶數更新。
- 明確地管理穩定和秩序。
1)何時使用Read Models(何時不使用)
適合:- 頻繁重讀(joins/聚合/排序),有效的更新延遲。
- Dashbords,目錄,登陸,「top-N」,個人圍裙,搜索列表。
- 負載分離:寫入核心-嚴格,讀取平面-快速且可擴展。
- 需要嚴格的「每個條目」不變量的操作(金錢,唯一性)。那裏有一個堅強的路徑。
2)建築輪廓(言語方案)
1.更改源:來自OLTP的域事件(事件源)或CDC。
2.投影輸送機:解析器→ aggregation/去規範化 → idempotent upsert。
3.Read Store:針對查詢優化的DB/索引 (RDBMS、柱形、搜索)。
4.API/客戶端:快速SELECT/GET,具有「as_of/freshness」屬性。
3) Read Model設計
從查詢開始: 哪些字段、過濾器、排序、分頁、前N?
去規範化:存儲已經合並的數據(名稱、金額、狀態)。
鑰匙:- 派對:通過「tenant_id」,日期,區域。
- 主要鑰匙:業務密鑰+時間桶(例如「(tenant_id,entity_id)」或「(tenant_id,bucket_minute)」)。
- 索引:按頻率where/order by。
- TTL/重建:用於臨時店面(例如90天)。
4)更新流程和平均水平
Idempotent upsert是投影穩定性的基礎。
偽造的: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);
規則:
- 每個消息都帶有版本/時間;僅接受「新鮮或平等」(idempotency)。
- 對於聚合體(計數器、和)-存儲狀態並使用交換式更新(或CRDT方法)。
5)更改源: 事件vs CDC
事件(事件來源):豐富的語義,易於構建不同的投影;圖的演變很重要。
CDC(邏輯復制):簡單地連接到現有數據庫;需要模擬DML→sobyty並過濾噪音升級。
- 「有毒」消息的交付保證(at least-once)和DLQ。
- 按鍵排序(partition key='tenant_id: entity_id')。
6)順序,因果關系和「新鮮」
按鍵順序:一個對象的事件必須依次出現;使用派對和版本。
因果關系(session/causal):讓作者看到他們的變化(RYW),在查詢中傳遞水印版本。
新鮮(bounded staleness):返回「as_of」/「X-Data-Freshness」並保持SLO(例如p95 ≤ 60 c)。
7)增量單元和前N
分鐘銷售箱示例: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;
對於前N:
- 維護排名的店面(例如「revenue DESC」),並且僅更新更改的位置(heap/skiplist/limited)。
- 存儲頂部的「窗口」(例如每段100-1000行)。
8)搜索和地理投影
搜索(ES/Opensearch):非歸一化文檔、轉換管道、文檔版本=源版本。
Geo:存儲「POINT/LAT, LON」,預聚合針腳/四邊形。
9)多特南特和地區
「tenant_id」在投影鍵和事件中具有約束力。
Fairness:限制per tenant投影(WFQ/DRR),以便「嘈雜」不會抑制其他投影。
居住地:投影與寫核生活在同一區域;區域間店面-總結/摘要。
10)可觀察性和SLO
度量標準:- 「projection_lag_ms」 (istochnik→vitrina)、「freshness_age_ms」(自上次三角洲以來)。
- 通過升級,錯誤比例,DLQ-rate, redrive-success.
- 店面大小,p95/p99閱讀潛伏期。
- Теги: `tenant_id`, `entity_id`, `event_id`, `version`, `projection_name`, `attempt`.
- 註釋:merge解決方案,跳過舊版本。
11)花花公子(runbooks)
1.拉格增長:檢查連接器/經紀人,增加分期付款,包括重點展示的優先級。
2.許多模式錯誤:凍結重做,執行模式遷移(backfill),重新啟動新版本的mapper。
3.重復DLQ:減少擊球,啟用「影子」處理程序,增強等效性。
4.店面不一致:從每窗口的日誌/來源執行重構店面(選擇性tenant/partition)。
5.熱鍵:限制按鍵競爭,添加本地隊列,將單元帶到單獨的店面。
12)完全重新計票(rebuild)和backfill
方法是:- 停止消費(或切換到新版本的店面)。
- 用包重新計算(按批次/日期/tenants)。
- 啟用兩階段卷軸:首先填充「read __ v2」,然後原子切換讀取路由。
13)方案的演變(轉化)
事件/文檔中的「schema_version」。
投影可以讀取多個版本,「即時」遷移。
對於重大變化-新的v2展示櫃和金絲雀流量。
14)安全和訪問
從源位置繼承RLS/ACL;不要在訪問方面比原始數據更廣泛。
將PII偽裝成不需要UX/分析的投影。
審核紅路/重新計票/手動編輯。
15)配置模板
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)典型錯誤
「每個案例一個展示櫃」→嚴重的升級和壞的p99。
單位→雙打/跳躍缺乏等效性。
雙寫直接進入店面和OLTP →差異。
零新鮮度可見性→與產品的期望沖突。
Rebuild沒有兩相卷軸→答案中的「孔」。
沒有分期付款/指數→價值上升和潛伏期上升。
17)快速食譜
目錄/搜索:文檔展示+增量upsert,lag ≤ 5-15 c,篩選器的索引。
Dashbords:分鐘/小時垃圾箱,「SUM/COUNT」單元,p95新鮮度≤ 60 c。
個人磁帶:作者按用戶+causal/RYW投影,後退到緩存。
全球SaaS: 區域店面,跨區域分組;fairness per tenant.
18)售前支票清單
- 陳列櫃的設計符合特定的要求;有指數和分期付款。
- 選擇更改源(事件/CDC);交付保證和按鍵順序。
- 具有版本/時間的Idempotent upsert;防止發生「舊」事件。
- 在答復(「as_of/freshness」)中定義並給出了新鮮度的SLO。
- DLQ和安全的重新分區配置;rebuild/backfill上的花花公子。
- 競爭限制(按鍵串行)和公平競爭限制。
- 滯後/錯誤/後退度量,p95/p99上的差值和DLQ的增長。
- 回收方案和遷移策略(v2+switch)。
- 訪問策略/PII已繼承和驗證。
結論
閱讀模型和投影是一種工程閱讀加速器:您付出「新鮮度」和流媒體基礎架構的少量代價,以獲得可預測的毫秒並卸載記錄核心。在查詢下設計店面,使升級變得令人望而生畏,測量時差,並明確承諾新鮮-即使負載,數據和地理位置增加,您的API仍將保持快速。