Database Sharding — Chiến Lược Phân Mảnh Dữ Liệu Khi Hệ Thống Vượt Ngưỡng
Posted on: 4/18/2026 1:11:32 AM
Mục lục
1. Tại sao cần Database Sharding?
Khi ứng dụng phát triển từ hàng nghìn lên hàng triệu người dùng, database đơn lẻ (single-node) sẽ đạt đến giới hạn về storage, throughput và latency. Vertical scaling (nâng cấp phần cứng) chỉ giải quyết được đến một mức nhất định — bạn không thể mua CPU vô hạn cho một máy chủ duy nhất.
Database Sharding (hay Horizontal Partitioning) là kỹ thuật chia một database lớn thành nhiều phần nhỏ hơn gọi là shard, mỗi shard chạy trên một server riêng biệt. Mỗi shard chứa một tập con (subset) của toàn bộ dữ liệu, nhưng có cùng schema.
graph TB
Client[👤 Client Application] --> Router[🔀 Shard Router / Proxy]
Router --> S1[📦 Shard 1
User ID 1-1M]
Router --> S2[📦 Shard 2
User ID 1M-2M]
Router --> S3[📦 Shard 3
User ID 2M-3M]
Router --> SN[📦 Shard N
User ID ...]
S1 --> R1[🔄 Replica 1a]
S2 --> R2[🔄 Replica 2a]
S3 --> R3[🔄 Replica 3a]
style Client fill:#e94560,stroke:#fff,color:#fff
style Router fill:#2c3e50,stroke:#fff,color:#fff
style S1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style S2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style S3 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style SN fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style R1 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style R2 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style R3 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Kiến trúc Database Sharding cơ bản với Shard Router và Read Replicas
2. Các chiến lược Sharding
2.1 Hash-based Sharding
Áp dụng một hàm hash lên shard key để xác định shard đích. Đây là chiến lược phổ biến nhất vì đảm bảo phân bổ dữ liệu đều đặn.
shard_id = hash(shard_key) % number_of_shards
-- Ví dụ: user_id = 12345
-- hash(12345) = 7829431
-- shard_id = 7829431 % 4 = 3 → Shard 3✅ Ưu điểm
Phân bổ đều dữ liệu, tránh hotspot. PlanetScale khuyến nghị hash-based là chiến lược mặc định cho hầu hết use case vì đơn giản và hiệu quả.
⚠️ Nhược điểm
Range query kém hiệu quả (phải scan tất cả shard). Khi thêm/bớt shard, cần rehash lại dữ liệu — đây là lý do Consistent Hashing ra đời.
2.2 Range-based Sharding
Chia dữ liệu theo khoảng giá trị liên tục của shard key. Phù hợp cho dữ liệu có tính thứ tự tự nhiên (timestamp, ID tuần tự, địa lý).
Ưu điểm: Range query rất nhanh (chỉ đọc 1-2 shard). Dễ hiểu, dễ quản lý.
Nhược điểm: Dễ tạo hotspot — shard chứa dữ liệu mới nhất luôn bị write nhiều nhất. Ví dụ: shard chứa orders tháng hiện tại chịu 90% traffic.
2.3 Directory-based Sharding
Dùng một bảng lookup (directory) riêng để map mỗi shard key → shard location. Linh hoạt nhất nhưng directory trở thành single point of failure.
graph LR
App[Application] --> Dir[(📋 Directory
Lookup Table)]
Dir --> |"user_id 1-500K"| S1[Shard 1]
Dir --> |"user_id 500K-800K"| S2[Shard 2]
Dir --> |"user_id 800K-2M"| S3[Shard 3]
Dir --> |"VIP users"| S4[Shard VIP]
style App fill:#e94560,stroke:#fff,color:#fff
style Dir fill:#2c3e50,stroke:#fff,color:#fff
style S1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style S2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style S3 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style S4 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Directory-based Sharding cho phép ánh xạ linh hoạt, kể cả logic đặc biệt (VIP shard)
Directory-based thường kết hợp với cache (Redis, Memcached) để giảm latency lookup. Khi cần di chuyển dữ liệu giữa các shard, chỉ cần cập nhật directory — không cần rehash.
2.4 Geographic Sharding
Phân bổ dữ liệu theo vị trí địa lý của user — dữ liệu ở gần nơi nó được truy cập nhất. Đặc biệt quan trọng cho ứng dụng global và yêu cầu data residency (GDPR, PDPA).
| Chiến lược | Phân bổ đều | Range Query | Resharding | Use case tiêu biểu |
|---|---|---|---|---|
| Hash-based | ⭐⭐⭐ | ⭐ | ⭐⭐ | User data, session store |
| Range-based | ⭐ | ⭐⭐⭐ | ⭐⭐⭐ | Time-series, log data |
| Directory-based | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | Multi-tenant SaaS |
| Geographic | ⭐⭐ | ⭐⭐ | ⭐⭐ | Global app, data residency |
3. Consistent Hashing & Virtual Nodes
Vấn đề lớn nhất của hash-based sharding đơn giản (hash(key) % N) là khi thêm hoặc bớt shard, hầu hết key phải được di chuyển (rehash). Với N = 4 shard thêm 1 shard thành N = 5, khoảng 80% dữ liệu phải migrate — không chấp nhận được ở production.
Consistent Hashing giải quyết vấn đề này bằng cách tổ chức hash space thành một vòng tròn (hash ring). Mỗi shard chiếm một vị trí trên ring, và mỗi key được map đến shard gần nhất theo chiều kim đồng hồ.
graph TB
subgraph Ring["🔵 Hash Ring (0 → 2³²)"]
direction TB
N1["🟢 Node A
position: 0°"]
N2["🔴 Node B
position: 90°"]
N3["🟡 Node C
position: 180°"]
N4["🟣 Node D
position: 270°"]
end
K1["Key X → hash: 45°"] -.->|"nearest clockwise"| N2
K2["Key Y → hash: 200°"] -.->|"nearest clockwise"| N4
K3["Key Z → hash: 350°"] -.->|"nearest clockwise"| N1
style N1 fill:#4CAF50,stroke:#fff,color:#fff
style N2 fill:#e94560,stroke:#fff,color:#fff
style N3 fill:#ff9800,stroke:#fff,color:#fff
style N4 fill:#9c27b0,stroke:#fff,color:#fff
style K1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style K2 fill:#f8f9fa,stroke:#9c27b0,color:#2c3e50
style K3 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Consistent Hashing Ring — thêm/bớt node chỉ ảnh hưởng key trên node lân cận
Khi thêm Node E vào vị trí 135°, chỉ các key trong khoảng (90°, 135°] phải di chuyển từ Node C sang Node E — chỉ ~25% dữ liệu của 1 node, thay vì 80% toàn hệ thống.
Virtual Nodes (VNodes)
Trong thực tế, chỉ 4-5 node vật lý trên ring sẽ tạo phân bổ không đều. Virtual Nodes giải quyết bằng cách mỗi node vật lý đảm nhận nhiều vị trí trên ring:
-- Node A có 3 virtual nodes tại vị trí: 0°, 120°, 240°
-- Node B có 3 virtual nodes tại vị trí: 40°, 160°, 280°
-- → Phân bổ đều hơn rất nhiều
-- Khi Node A bị remove:
-- Dữ liệu tại 0° → chuyển sang virtual node gần nhất (Node B tại 40°)
-- Dữ liệu tại 120° → chuyển sang Node C tại 160°
-- Dữ liệu tại 240° → chuyển sang Node B tại 280°
-- → Load được phân tán đều cho các node còn lại💡 Thực tiễn
Cassandra sử dụng 256 virtual nodes mặc định cho mỗi node vật lý. DynamoDB của Amazon cũng dùng consistent hashing với virtual nodes cho partition management. Khi thiết kế mới, khuyến nghị bắt đầu với 128-256 VNodes/node để đảm bảo phân bổ đều ngay từ đầu.
4. Nghệ thuật chọn Shard Key
Shard key là quyết định quan trọng nhất khi thiết kế sharding — chọn sai sẽ tạo hotspot, cross-shard query tràn lan, và việc sửa lại cực kỳ tốn kém. Dưới đây là các tiêu chí cốt lõi:
4.1 High Cardinality
Shard key phải có nhiều giá trị phân biệt. user_id (hàng triệu giá trị) tốt hơn country_code (chỉ ~200 giá trị). Cardinality thấp nghĩa là dữ liệu sẽ tập trung vào ít shard.
4.2 Even Distribution
Dữ liệu phải phân bổ đều giữa các shard. Ví dụ created_date là shard key xấu cho range-based vì dữ liệu mới luôn dồn vào shard cuối.
4.3 Query Pattern Alignment
Shard key nên match với WHERE clause phổ biến nhất. Nếu 90% query filter theo tenant_id, đó nên là shard key — mỗi query chỉ hit 1 shard.
4.4 Stability (Immutable)
Shard key không nên thay đổi. Nếu user đổi email và email là shard key, row phải migrate sang shard khác — tốn kém và dễ lỗi.
| Shard Key | Cardinality | Distribution | Stability | Đánh giá |
|---|---|---|---|---|
user_id | Rất cao | Đều (hash) | Không đổi | ⭐ Tốt nhất cho multi-user app |
tenant_id | Trung bình | Có thể lệch | Không đổi | ⭐ Tốt cho SaaS, cần monitor size |
order_id | Rất cao | Đều | Không đổi | ⭐ Tốt cho e-commerce |
created_at | Cao | Lệch (mới = nóng) | Không đổi | ⚠️ Chỉ tốt cho archive data |
country | Thấp (~200) | Rất lệch | Có thể đổi | ❌ Tránh dùng một mình |
email | Cao | Đều | Có thể đổi | ❌ Không ổn định |
⚠️ Compound Shard Key
Khi không có shard key đơn nào hoàn hảo, dùng compound key. Ví dụ: (tenant_id, user_id) đảm bảo dữ liệu cùng tenant nằm trên cùng shard (tốt cho isolation), đồng thời hash trên user_id để phân bổ đều trong tenant lớn.
5. Thách thức khi Sharding
5.1 Cross-Shard Queries
Query cần dữ liệu từ nhiều shard là thách thức lớn nhất. Ví dụ: SELECT * FROM orders WHERE product_id = 42 khi shard key là user_id — phải scatter query đến TẤT CẢ shard rồi gather kết quả.
sequenceDiagram
participant App as Application
participant Router as Shard Router
participant S1 as Shard 1
participant S2 as Shard 2
participant S3 as Shard 3
App->>Router: SELECT * FROM orders WHERE product_id = 42
Router->>S1: Forward query
Router->>S2: Forward query
Router->>S3: Forward query
S1-->>Router: 15 rows
S2-->>Router: 8 rows
S3-->>Router: 22 rows
Router->>Router: Merge + Sort + Limit
Router-->>App: Final result (45 rows)
Scatter-Gather pattern — cross-shard query phải fan out đến tất cả shard
Giải pháp: Thiết kế shard key theo query pattern chính. Nếu cần query theo nhiều dimension, dùng denormalization (duplicate data across shard) hoặc secondary index shard riêng.
5.2 Distributed Transactions
Transaction span nhiều shard cần giao thức phân tán như 2-Phase Commit (2PC) — chậm và phức tạp. Ưu tiên thiết kế sao cho transaction chỉ nằm trong 1 shard.
-- ❌ Cross-shard transaction (chậm, phức tạp)
BEGIN DISTRIBUTED TRANSACTION
UPDATE shard_1.accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE shard_3.accounts SET balance = balance + 100 WHERE user_id = 999;
COMMIT
-- ✅ Thiết kế tốt hơn: dùng Saga pattern
-- Step 1: Debit user 1 (shard 1) → emit event
-- Step 2: Credit user 999 (shard 3) → consume event
-- Step 3: Nếu step 2 fail → compensating transaction ở shard 15.3 Resharding (Rebalancing)
Khi dữ liệu tăng và cần thêm shard, việc di chuyển dữ liệu giữa các shard (resharding) cực kỳ tốn kém. Các bước thường gặp:
- Double-write: Ghi song song vào cả shard cũ và mới
- Backfill: Copy dữ liệu lịch sử từ shard cũ sang shard mới
- Verify: So sánh checksum giữa 2 bên
- Cutover: Chuyển traffic sang schema shard mới
- Cleanup: Xóa dữ liệu dư thừa ở shard cũ
✅ Pre-splitting
Nếu hôm nay cần 3 shard, hãy bắt đầu với 4 (power of 2). Consistent hashing hoạt động tốt hơn khi số node là lũy thừa 2. Nhiều team bắt đầu với 8-16 logical shard dù chỉ cần 2-3 physical server — mỗi server host nhiều logical shard, khi cần scale chỉ cần di chuyển logical shard sang server mới.
5.4 Unique Constraints Across Shards
Đảm bảo UNIQUE constraint toàn cục (ví dụ: email unique trên tất cả shard) không thể dùng database constraint đơn giản. Giải pháp:
- Separate uniqueness table: Một bảng nhỏ centralized chỉ chứa (email → shard_id)
- Globally unique ID: Snowflake ID, UUID, hoặc ULID encode shard info vào ID
- Application-level check: Query trước khi insert, chấp nhận race condition hiếm
6. So sánh giải pháp: Vitess, Citus, CockroachDB, TiDB
Năm 2026, có 4 giải pháp sharding nổi bật, mỗi cái phục vụ use case khác nhau:
| Tiêu chí | Vitess | Citus | CockroachDB | TiDB |
|---|---|---|---|---|
| Nền tảng | MySQL middleware | PostgreSQL extension | Custom (Postgres-compatible) | Custom (MySQL-compatible) |
| Sharding model | Explicit (manual key) | Distributed tables | Automatic range-based | Automatic (Region-based) |
| Resharding | Manual, online | Rebalancer tool | Tự động | Tự động (split/merge) |
| Cross-shard query | Hạn chế | Tốt | Rất tốt | Rất tốt |
| Consistency | Per-shard (MySQL) | Per-node (Postgres) | Strong (Raft) | Strong (Raft) |
| HTAP | Không | Columnar addon | Không | Có (TiFlash) |
| Triển khai | Kubernetes-native | Managed + self-host | Managed + self-host | Managed + self-host |
| Xuất phát | YouTube/Google | Microsoft (Azure) | Cockroach Labs | PingCAP |
| Best for | MySQL scale-out có kiểm soát | Postgres + analytics | Multi-region, Postgres-first | MySQL-first, HTAP workload |
graph TB
subgraph Decision["🤔 Chọn giải pháp nào?"]
Start{Database hiện tại?}
Start -->|MySQL| MySQL_Q{Cần auto-sharding?}
Start -->|PostgreSQL| PG_Q{Cần multi-region?}
Start -->|Mới hoàn toàn| New_Q{Ưu tiên gì?}
MySQL_Q -->|Có| TiDB_R[✅ TiDB]
MySQL_Q -->|Không, muốn kiểm soát| Vitess_R[✅ Vitess]
PG_Q -->|Có| CRDB_R[✅ CockroachDB]
PG_Q -->|Không| Citus_R[✅ Citus]
New_Q -->|HTAP| TiDB_R2[✅ TiDB]
New_Q -->|Multi-region| CRDB_R2[✅ CockroachDB]
New_Q -->|Postgres ecosystem| Citus_R2[✅ Citus]
end
style Start fill:#e94560,stroke:#fff,color:#fff
style MySQL_Q fill:#2c3e50,stroke:#fff,color:#fff
style PG_Q fill:#2c3e50,stroke:#fff,color:#fff
style New_Q fill:#2c3e50,stroke:#fff,color:#fff
style TiDB_R fill:#4CAF50,stroke:#fff,color:#fff
style TiDB_R2 fill:#4CAF50,stroke:#fff,color:#fff
style Vitess_R fill:#4CAF50,stroke:#fff,color:#fff
style CRDB_R fill:#4CAF50,stroke:#fff,color:#fff
style CRDB_R2 fill:#4CAF50,stroke:#fff,color:#fff
style Citus_R fill:#4CAF50,stroke:#fff,color:#fff
style Citus_R2 fill:#4CAF50,stroke:#fff,color:#fff
Decision tree chọn giải pháp sharding phù hợp
Case study: Ninja Van chọn TiDB thay vì Vitess
Ninja Van — nền tảng logistics Đông Nam Á xử lý hàng triệu đơn hàng/ngày — đã chuyển từ MySQL sang TiDB thay vì chọn Vitess hoặc CockroachDB. Lý do chính:
- TiDB tương thích MySQL protocol → migration cost thấp
- Auto-sharding không cần define shard key thủ công
- TiFlash cho phép chạy analytics trực tiếp trên operational data (HTAP)
- Horizontal scale trên Kubernetes không cần resharding thủ công
7. Khi nào KHÔNG nên Sharding?
Sharding thêm complexity đáng kể. Trước khi shard, hãy chắc chắn đã thử hết các giải pháp đơn giản hơn:
⚠️ Sharding Premature là Anti-Pattern
Nhiều team shard quá sớm vì "phòng xa". Thực tế: một PostgreSQL single-node trên hardware hiện đại (2026) xử lý tốt tới vài TB dữ liệu và hàng chục nghìn transaction/giây. Sharding khi chưa cần tạo ra overhead về operational complexity mà không mang lại giá trị.
8. Kết luận
Database Sharding là công cụ mạnh mẽ nhưng đi kèm trade-off lớn về complexity. Hãy nhớ:
- Hash-based là chiến lược mặc định tốt nhất cho phần lớn use case
- Consistent Hashing với Virtual Nodes giải quyết bài toán resharding
- Shard Key phải có high cardinality, stable, và match query pattern chính
- Cross-shard query luôn đắt — thiết kế schema để tối thiểu chúng
- Giải pháp hiện đại như TiDB, CockroachDB cung cấp auto-sharding, giảm gánh nặng vận hành
- Luôn thử query optimization → read replica → caching → vertical scaling trước khi nghĩ đến sharding
Trong thời đại distributed systems 2026, hiểu sharding không chỉ để áp dụng — mà còn để biết khi nào không cần nó. Đó mới là dấu hiệu của một kiến trúc sư hệ thống trưởng thành.
Nguồn tham khảo:
- PlanetScale — Sharding Strategies: Directory-based, Range-based, and Hash-based
- PingCAP — Why TiDB Beats Vitess and CockroachDB at Ninja Van
- Last9 — Database Sharding: How It Works and When You Actually Need It
- Hello Interview — Sharding in System Design Interviews
- Brandur — A Comparison of Advanced, Modern Cloud Databases
Zero-Downtime Deployment — Blue-Green, Canary và Rolling Update
Cloudflare Agent Cloud 2026 — Xây dựng AI Agents trên Edge với Workers, Durable Objects và Project Think
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.