Part 04 · PostgreSQL & Redis · 4.1.05

Schema, query và connection design

Schema là API lâu dài cho mọi writers. Constraints, types, access patterns, connection capacity và migration sequence phải cùng bảo vệ correctness lẫn khả năng rollout.


Dễ nhầm cardinality? Đọc 4.1.05A Database Relations & JPA Mapping để xác định foreign key, owning side và phân biệt one-to-many với many-to-one.

Constraints bảo vệ invariant

Bắt đầu từ invariant cụ thể: balance không âm, email unique, order chỉ chuyển state hợp lệ và một idempotency key chỉ tạo một payment. NOT NULL, CHECK, UNIQUE, primary key, foreign key và exclusion constraints bảo vệ dữ liệu cho mọi writer. Application validation cải thiện UX nhưng không thay database constraint. Deferred constraints hữu ích cho một số multi-row transitions nhưng làm failure xuất hiện muộn và tăng transaction complexity.

English interview answer: “I start data design from business invariants and access patterns. I enforce invariants at the strongest practical layer, often including database constraints, because application-only validation can race under concurrent requests.”

Normalization và denormalization

Normalization tách facts để tránh update, delete và insert anomalies. Denormalization là optimization phải có source of truth, sync owner, rebuild path và consistency model. JSON aggregate không thay relational model nếu fields cần join, constraint hoặc query thường xuyên.

English interview answer: “I normalize transactional source-of-truth data by default. I denormalize when measured read patterns justify it and when duplicated data has a clear consistency mechanism, such as transactional updates, change events or rebuildable projections.”

Chọn data types

DomainLựa chọnCaveat
Moneynumeric hoặc integer minor units theo precision/range.Không dùng floating point cho exact financial values.
Timetimestamptz cho instant; date cho calendar date.Timezone presentation thuộc boundary; DST cần test.
StatusDB enum, text + check hoặc reference table.Enum chặt nhưng migration có trade-off; text linh hoạt hơn nhưng cần constraint.
IdentifierUUID hoặc identity/bigint.Random UUID ảnh hưởng index locality; sequential ID đơn giản nhưng dễ đoán và khó generate phân tán.

JSONB

JSONB phù hợp attributes linh hoạt hoặc document payload; GIN và operators hỗ trợ search mạnh. Business fields quan trọng vẫn nên có typed columns và constraints. Update một phần JSON vẫn tạo row version mới và WAL; schema evolution không biến mất chỉ vì dữ liệu là document.

Chọn SQL, document hay key-value storage

Nhu cầu chínhCandidateCâu hỏi quyết định
Relations, constraints, flexible joins/reportingRelational databaseInvariant và multi-row transaction nào phải được bảo vệ?
Aggregate/document đọc-ghi cùng nhau, nested shape biến đổiDocument databaseAggregate có bounded size/lifecycle và ít cross-document joins không?
Simple key access, latency rất thấpKey-value storeConsistency, durability, scan/query và failure model có đủ không?
Massive time-series/write patternColumn-family hoặc time-series storePartition key, retention và query dimensions là gì?
Relationship traversal là core queryGraph databaseTraversal depth/pattern có justify engine chuyên biệt không?

Với MongoDB, embed khi child được đọc/cập nhật cùng aggregate, cùng lifecycle và bounded growth. Reference khi relation many-to-many, child được chia sẻ, update độc lập hoặc collection có thể tăng vô hạn. “Schema linh hoạt” không loại nhu cầu validation, indexes và migrations.

Pagination

Offset dễ dùng nhưng chậm và không ổn định ở page sâu khi dataset thay đổi. Keyset cursor chứa last ordering values, dùng lexicographic predicate và unique tie-breaker. Cursor nên opaque/versioned; snapshot semantics và direction phải rõ.

Connection pool

Pool size dựa database concurrent capacity và query latency, không dựa số HTTP requests. Acquisition timeout phải ngắn hơn request deadline. Idle/max lifetime giúp thay stale connections; leak detection chỉ là diagnostic. PgBouncer transaction pooling có giới hạn với session state, temp tables và một số prepared-statement behaviors.

Queue multiplication: application instances × pool size là tổng potential DB sessions. Autoscaling application mà giữ pool cố định mỗi pod có thể overload database dù từng pod nhìn bình thường.
English interview answer: “A connection pool limits and reuses expensive database connections; it does not create database capacity. I size it across all service replicas and monitor acquisition wait, active sessions, transaction duration and database saturation.”

Zero-downtime migrations

  1. Expand: thêm schema nullable hoặc backward-compatible.
  2. Deploy code đọc/ghi tương thích cả cũ lẫn mới.
  3. Backfill theo bounded batches, có progress và retry.
  4. Validate constraints/indexes khi dữ liệu đã sạch.
  5. Chuyển read/write path và quan sát.
  6. Contract: xóa schema cũ sau compatibility window.

Đánh giá DDL lock và table rewrite trước rollout. Migration phải audited và resumable; backup không thay rollback/forward-fix plan vì restore có thể vượt RTO và mất dữ liệu sau backup.

Nguồn tham khảo