모델 및 프로젝션 읽기
읽기 모델은 특정 제품 시나리오에 대한 빠른 읽기를 위해 특별히 설계된 테이블/색인/뷰입니다. 프로젝션은 소스 이벤트/변경 사항을 읽기 모델 업데이트 (일반적으로 dempotent upsert) 로 변환하는 프로세스입니다. CQRS와 함께 OLTP 코어를 내리고 p95/p99 판독을 안정화하여 "신선도" 를 제어 할 수 있습니다.
주요 아이디어:- "범용 제도" 가 아니라 요청에 따라 거부.
- 점진적으로 그리고 엄청나게 업데이트하십시오
- 정체성과 질서를 명시 적으로 관리합니다.
1) 읽기 모델 사용시기 (및 그렇지 않은 경우)
적합:- 허용 가능한 업데이트 대기 시간이있는 빈번한 무거운 읽기 (가입/집계/정렬).
- 대시 보드, 카탈로그, 방문 페이지, "top-N", 개인 피드, 검색 목록.
- 로드 공유: 쓰기 코어-엄격하고 읽기 평면-빠르고 확장 가능합니다.
- 엄격한 불변량이 필요한 작업 "입력 당" (돈, 독창성). 강한 길이 있습니다.
2) 건축 개요 (언어 체계)
1. 변경 사항: OLTP의 도메인 이벤트 (이벤트 소싱) 또는 CDC 이벤트.
2. 투영 파이프 라인: parser → 집계/비정규화 → demempotent upsert.
3. 읽기 저장소: 쿼리에 최적화 된 데이터베이스/색인 (RDBMS, 열, 검색).
4. API/클라이언트: "as _ of/freshness" 속성을 가진 빠른 SELECT/GET.
3) 모델 디자인 읽기
쿼리로 시작하십시오: 어떤 필드, 필터, 정렬, 페이지 매김, 상단 N?
Denormalize: 이미 병합 된 데이터 (이름, 금액, 상태) 를 저장합니다.
- 파티션: '테넌트 _ id', 날짜, 지역별.
- 기본 키: 비즈니스 키 + 타임 버킷 (예: '(테넌트 _ id, 엔티티 _ id)' 또는 '(테넌트 _ id, 버킷 _ 분)').
- 색인: 빈번한 위치/주문.
- TTL/보존: 임시 디스플레이 사례 (예: 90 일).
4) 흐름 및 demmpotency 업데이트
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);
규칙:
- 각 메시지에는 버전/시간이 있습니다. "신선하거나 동등한" (demempotency) 만 허용합니다.
- 집계 (카운터, 합계) 의 경우-상태를 저장하고 정류 업데이트 (또는 CRDT 접근 방식) 를 사용하십시오.
5) 변화의 출처: 이벤트 vs CDC
이벤트 (이벤트 소싱): 풍부한 의미론, 다른 프로젝션을 쉽게 구축 할 수 있습니다. 회로의 진화가 중요합니다.
CDC (논리 복제): 간단히 기존 데이터베이스에 연결하십시오. DML → sobyty 매핑 및 노이즈 업데이트 필터링이 필요합니다.
두 옵션 모두 다음이 필요합니
"유독 한" 메시지에 대한 배송 보장 (적어도 한 번) 및 DLQ.
키별로 주문 (파티션 키 = 'tenant _ id: 엔티티 _ id').
6) 질서, 인과 관계 및 "신선도"
키별로: 한 객체의 이벤트는 순차적으로 와야합니다. 파티셔닝 및 버전을 사용하십시오
세션/인과: 저자가 변경 사항 (RYW) 을 보려면 워터 마크 버전을 쿼리로 전달하십시오.
경계 정체: 'of _ of '/' X-Data-Freshness' 로 반환하고 SLO 보유 (예: p95 소 60c).
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;
Top N의 경우:
- 순위 쇼케이스 (예: '수익 DESC') 를 유지하고 변경된 위치 (힙/건너 뛰기/제한 테이블) 만 업데이트하십시오.
- 상단의 "창" 을 저장하십시오 (예: 세그먼트 당 100-1000 줄).
8) 검색 및 지리 투영
검색 (ES/OpenSearch): 비정규화 된 문서, 파이프 라인 변환, 문서 버전 = 소스 버전.
지오: 'POINT/LAT, LON', 사전 집계 타일/쿼드를 저장하십시오.
9) 멀티 테넌트 및 지역
프로젝션 키 및 이벤트에는 '테넌트 _ id' 가 필요합니다.
공정성: "잡음" 이 나머지 속도를 늦추지 않도록 테넌트 당 투영 처리량 (WFQ/DRR) 을 제한하십시오.
거주지: 투영은 쓰기 코어와 같은 지역에 있습니다. 지역 간 쇼케이스-집계/요약.
10) 관찰 및 SLO
메트릭:- 'project _ lag _ ms' (istochnik → vitrina), 'freshness _ age _ ms' (마지막 델타 이후).
- 업데이트 처리, 오류율, DLQ 속도, 재 구동 성공.
- 창 크기, p95/p99 판독 대기 시간.
- 지정 번호: '테넌트 _ id', '엔티티 _ id', '이벤트 _ id', '버전', '투영 _ 네임', '시도'.
- 주석: 솔루션 병합, 오래된 버전의 생략.
11) 플레이 북 (런북)
1. 성장 지연: 커넥터/브로커 확인, 당사자 증가, 주요 쇼케이스 우선 순위 지정 등이 있습니다.
2. 많은 스키마 오류: 리드라이브 동결, 스키마 마이그레이션 (백필), 새 버전의 매퍼로 다시 시작하십시오.
3. 반복 된 DLQ: 배치를 줄이고 "그림자" 핸들러를 활성화하며 demempotency를 증가시킵니다.
4. 창 불일치: 창 당 로그/소스에서 창을 다시 만듭니다 (테넌트/파티션 선택).
5. 핫 키: 키별로 경쟁을 제한하고 로컬 대기열을 추가하고 별도의 쇼케이스에 장치를 배치하십시오.
12) 전체 재 계산 (재건) 및 백필
접근 방식:- 소비 중지 (또는 새로운 버전의 쇼케이스로 전환).
- 배치/날짜/테넌트별로 재구성하십시오.
- 2 단계 스위치 사용: 먼저 '읽기 _ _ v2' 를 채운 다음 읽기 라우팅을 원자 적으로 전환하십시오.
13) 회로의 진화 (버전)
'스키마 _ 버전' 이벤트/문서.
프로젝션은 여러 버전을 읽고 즉시 마이그레이션 할 수 있습니다
주요 변경 사항-새로운 v2 쇼케이스 및 카나리아 트래픽.
14) 보안 및 액세스
소스에서 상속 RLS/ACL; 쇼케이스를 원래 데이터보다 더 넓게 액세스하지 마십시오.
UX/분석에 필요하지 않은 프로젝션의 마스크 PII.
재구성/재 계산/수동 편집 감사.
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.
dempotency → 부족은 집계로 복제/점프합니다.
쇼케이스 및 OLTP → 불일치에 직접 이중 쓰기.
신선도의 가시성이 제로이므로 제품과 기대치가 상충됩니다.
답변에서 2 상 스위치없이 → "홀" 을 다시 구축하십시오.
분할/인덱스 → 비용 및 대기 시간 성장이 없습니다.
17) 빠른 레시피
카탈로그/검색: 문서 쇼케이스 + 증분 확대, 지연 5-15 초, 필터 색인.
대시 보드: 분/시간 탱크, 'SUM/COUNT' 장치, p95 신선도
개인 테이프: 저자를위한 사용자 + 인과/RYW에 의한 투영, 캐시로 대체.
Global SaaS: 지역 쇼케이스, 지역 간 집계; 임차인 당 공정성.
18) 사전 판매 점검표
- 쇼케이스는 특정 요청을 위해 설계되었습니다. 지수와 당사자가 있습니다.
- 선택된 변경 출처 (이벤트/CDC); 배송 보증 및 주요 주문.
- 버전/시간에 대한 이념적 확신; "오래된" 이벤트에 대한 보호.
- 신선도 SLO가 정의되고 응답됩니다 ('_ of/freshness').
- DLQ 및 보안 릴리스 구성; 재건/백필에 대한 플레이 북.
- 세입자 당 키 당 연속 및 공정성.
- 래그/오류/대기 시간 지표, p95/p99 경고 및 DLQ 성장.
- 서킷 버전 지정 및 마이그레이션 전략 (v2 + 스위치).
- 액세스/PII 정책은 상속 및 검증됩니다.
결론
읽기 모델 및 프로젝션은 읽기의 엔지니어링 액셀러레이터입니다. "신선도" 및 스트리밍 인프라에 대해 적은 가격을 지불하여 예측 가능한 밀리 초를 얻고 녹음의 핵심을 오프로드합니다. 디자인 상점은 귀하의 요청에 맞게 업데이트를 업데이트하고 지연을 측정하며 신선도를 명확하게 약속합니다. API는로드, 데이터 및 지리가 증가하더라도 빠르게 유지됩니다.