🗄️ Database✍️ Khoa📅 19/04/2026☕ 6 phút đọc

Database: Replication, Sharding và Distributed Transactions

11. Replication

11.1 Replication Modes

Tradeoff cốt lõi của replication: latency vs data safety.

Asynchronous Replication: primary commit và trả lời client ngay, rồi replication xảy ra sau. Writes nhanh, nhưng nếu primary crash trước khi replica nhận được data → mất data.

Synchronous Replication: primary phải đợi replica confirm trước khi commit. Zero data loss, nhưng write latency = latency mạng đến replica. Một replica chậm hoặc chết sẽ block mọi writes.

Semi-synchronous (MySQL): đợi ít nhất 1 replica confirm nhận WAL (không phải apply xong). Cân bằng tốt giữa safety và performance — đây là option phổ biến nhất trong production.

11.2 Replication Methods

Statement-Based Replication (SBR): replicate SQL statements. Compact, nhưng non-deterministic functions (NOW(), RAND()) cho kết quả khác nhau trên replica → data inconsistency.

Row-Based Replication (RBR): replicate actual row changes (before/after images). Deterministic và an toàn hơn, nhưng binlog lớn hơn khi có bulk operations (UPDATE 1M rows → 1M row images).

Mixed: SBR mặc định, tự động chuyển sang RBR khi phát hiện non-deterministic operations.

WAL Shipping (PostgreSQL): gửi WAL segments đến replica. Physical replication — replica là exact byte copy của primary. Không thể replicate select tables hay cross-version.

Logical Replication (PostgreSQL): replicate logical changes (insert/update/delete). Linh hoạt hơn: có thể chọn table cụ thể, replicate cross-version, dùng cho zero-downtime migration.

11.3 Replication Lag

Lag là khoảng thời gian replica chậm hơn primary. Đây là vấn đề thực tế quan trọng:

Nguyên nhân:
  - Network latency
  - Replica apply chậm (single-threaded apply trong phiên bản cũ)
  - Large transactions (phải đợi commit xong mới replicate)

Hậu quả:
  Read-your-writes violation:
    → User write vào primary
    → Đọc từ replica ngay sau
    → Thấy data cũ → confusing UX

Giải pháp: đọc từ primary ngay sau write; hoặc track replication position và chỉ đọc replica khi đã caught up đến position đó; hoặc dùng "monotonic reads" — user luôn đọc từ cùng một replica.

Monitor lag:
  MySQL: SHOW SLAVE STATUS → Seconds_Behind_Master
  PostgreSQL: pg_stat_replication → write_lag, flush_lag, replay_lag

11.4 Failover

Automatic Failover: khi primary down, hệ thống tự detect (heartbeat timeout), bầu chọn replica có replication position cao nhất làm primary mới, redirect writes.

Split-Brain: nguy hiểm nhất trong failover. Network partition khiến cả hai nodes nghĩ mình là primary → cả hai accept writes → data diverge. Giải pháp: dùng STONITH (Shoot The Other Node In The Head) — node không chắc chắn về vai trò của mình thì tự tắt hoặc fence node kia. Cần majority quorum để promote.

Công cụ phổ biến: Patroni (PostgreSQL), MySQL Router + Group Replication, AWS RDS Multi-AZ (managed, tự động).


12. Sharding / Partitioning

12.1 Vertical vs Horizontal

Vertical Scaling (Scale Up): nâng cấp phần cứng — RAM, CPU, SSD nhanh hơn. Đơn giản nhất vì không cần thay đổi application, nhưng có ceiling và đắt.

Vertical Partitioning: tách theo columns hoặc theo service boundary. Ví dụ: tách user_profileuser_preferences table, hoặc tách hẳn user DB, order DB, product DB.

Horizontal Sharding: tách rows ra nhiều machines. Mỗi shard là một DB instance độc lập. Horizontally scalable không giới hạn, nhưng application phải biết shard nào chứa data nào.

12.2 Sharding Strategies

Range-Based Sharding: chia theo range của shard key (ví dụ user_id 1–1M → Shard 1). Trực quan và range queries hiệu quả, nhưng dễ bị hotspot: new users luôn đến shard mới nhất.

Hash-Based Sharding: shard_id = hash(key) % num_shards. Phân bổ đều, nhưng range queries phải hit tất cả shards. Resharding (thêm shard) cực tốn kém — phải remap gần như toàn bộ keys.

Consistent Hashing: servers và keys đều map lên một ring [0, 2^32). Key được serve bởi server đầu tiên theo chiều kim đồng hồ. Thêm/bớt server chỉ remap ~1/N keys. Dùng virtual nodes để đảm bảo distribution đều.

Directory-Based Sharding: lookup table user_id → shard_id. Flexible nhất và dễ migrate, nhưng lookup table trở thành SPOF và thêm latency.

12.3 Hotspots & Solutions

Celebrity Problem: user nổi tiếng (hoặc hot content) → hàng triệu requests → 1 shard quá tải. Giải pháp: cache aggressively (CDN, Redis), hoặc replicate hot data ra nhiều shards.

Time-based Hotspot: posts mới nhất luôn đến shard gần nhất (nếu range-based). Giải pháp: write to multiple shards và merge on read, hoặc dùng hash-based cho shard key.

12.4 Cross-Shard Operations

Đây là điểm đau lớn nhất của sharding:

Cross-Shard Query: phải query nhiều shards rồi merge kết quả ở application layer. Không có query optimizer nào làm điều này cho bạn.

Cross-Shard JOIN: không thể JOIN trực tiếp. Phải denormalize (duplicate data vào cùng shard), hoặc application-level join (tốn N+1 queries).

Resharding (zero-downtime):

1. Double-write: ghi vào cả shard cũ và mới
2. Backfill: copy data cũ sang shard mới
3. Verify consistency
4. Switch reads sang shard mới
5. Dừng ghi vào shard cũ

12.5 Native Partitioning (trong 1 DB instance)

Partitioning ở tầng DB — không phải sharding (vẫn 1 instance, 1 DB server), nhưng data được chia thành các partition nhỏ hơn.

CREATE TABLE orders (
    id BIGINT,
    created_at TIMESTAMP,
    total DECIMAL
) PARTITION BY RANGE (created_at);

CREATE TABLE orders_2024_q1
    PARTITION OF orders
    FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');

Lợi ích: partition pruning (query chỉ đọc partition liên quan), DROP partition instant thay vì DELETE hàng triệu rows, index per partition nhỏ hơn và rebuild nhanh hơn.


13. Distributed Transactions

13.1 Two-Phase Commit (2PC)

2PC là cách cổ điển để đảm bảo atomicity khi transaction span nhiều shards:

Phase 1 — Prepare:
  Coordinator → PREPARE → Shard 1 và Shard 2
  Shard 1 → PREPARED (YES) → Coordinator  (lock resources, write to WAL)
  Shard 2 → PREPARED (YES) → Coordinator

Phase 2 — Commit:
  Coordinator → COMMIT → Shard 1 và Shard 2
  Shard 1, 2 → ACK → Coordinator

Nếu bất kỳ shard nào vote NO:
  Coordinator → ROLLBACK → tất cả shards

Vấn đề blocking: nếu coordinator crash sau PREPARE nhưng trước COMMIT, các shards đã lock resources nhưng không biết phải commit hay rollback → "in-doubt transaction" → hold locks vô thời hạn. Cần manual intervention hoặc timeout.

2PC là blocking protocol với SPOF ở coordinator — không phù hợp cho high-availability production systems.

13.2 Saga Pattern — Alternative

Thay vì 1 distributed transaction lớn, Saga chia thành nhiều local transactions nhỏ, mỗi bước có compensating transaction để undo.

Order flow:
  Step 1: Create order (OrderService)           → Compensate: Cancel order
  Step 2: Reserve inventory (InventoryService)  → Compensate: Release inventory
  Step 3: Charge payment (PaymentService)       → Compensate: Refund
  Step 4: Schedule delivery (DeliveryService)   → Compensate: Cancel delivery

Nếu Step 3 thất bại:
  → Chạy compensating transactions ngược lại
  → Release inventory → Cancel order

Choreography-based: services emit events, services khác lắng nghe và react. Không có central coordinator → loose coupling, nhưng khó debug vì không có global state.

Orchestration-based: central orchestrator điều phối từng step. Dễ monitor và trace hơn, nhưng orchestrator có thể trở thành bottleneck.

Saga trade off durability của ACID để đổi lấy availability — phù hợp với microservices nhưng cần thiết kế compensating transactions cẩn thận.