Giao diện
Scalability — Thiết kế hệ thống chịu tải
Tháng 11/2020, Disney+ ra mắt tại châu Âu và sập ngay ngày đầu tiên — 10 triệu người dùng đổ vào cùng lúc, gấp 5x dự kiến. Không phải vì kỹ sư Disney kém, mà vì họ đánh giá sai capacity cần thiết và scaling strategy không kịp phản ứng.
Scalability không phải là "thêm server". Nó là nghệ thuật dự đoán bottleneck trước khi nó xảy ra, chọn đúng chiến lược mở rộng cho từng layer, và thiết kế hệ thống sao cho việc scale không đòi hỏi viết lại từ đầu. Một hệ thống scalable tốt có thể tăng 10x traffic mà chỉ cần thay đổi configuration, không phải architecture.
Bài này trang bị cho bạn tư duy capacity planning — kỹ năng mà mọi senior engineer đều cần nhưng ít người được dạy bài bản.
Bức tranh tư duy
Hãy tưởng tượng một quán phở Sài Gòn.
Khi quán đông, chủ quán có hai lựa chọn: thuê đầu bếp giỏi hơn (vertical scaling) hoặc mở thêm chi nhánh (horizontal scaling). Thuê đầu bếp giỏi hơn thì đơn giản — không cần thay đổi quy trình. Nhưng đầu bếp giỏi nhất thế giới vẫn chỉ nấu được N tô/giờ. Mở chi nhánh thì phức tạp hơn — cần chuẩn hoá công thức, chia nguyên liệu, đồng bộ menu — nhưng về lý thuyết không có giới hạn.
Nhưng analogy này có giới hạn: trong phần mềm, "mở chi nhánh" phức tạp hơn nhiều so với đời thực — bạn phải xử lý data consistency, network latency, và distributed failures. Đừng để sự đơn giản của analogy che giấu complexity thực sự.
Cốt lõi kỹ thuật
Vertical vs Horizontal Scaling
| Tiêu chí | Vertical (Scale Up) | Horizontal (Scale Out) |
|---|---|---|
| Cách làm | Nâng cấu hình máy: CPU, RAM, SSD | Thêm máy vào cluster |
| Giới hạn | Hardware ceiling (~256 cores, ~12TB RAM) | Lý thuyết: không giới hạn |
| Code changes | Không cần | Cần thiết kế stateless |
| Consistency | Đơn giản (single node) | Phức tạp (distributed) |
| Downtime khi scale | Thường cần restart | Zero-downtime nếu thiết kế đúng |
| Chi phí | Tăng theo cấp số nhân | Tăng tuyến tính |
| Fault tolerance | SPOF (Single Point of Failure) | Built-in redundancy |
Quy tắc vàng: Scale vertically cho đến khi chi phí không còn hợp lý hoặc chạm giới hạn phần cứng, sau đó mới scale horizontally. Premature horizontal scaling là complexity không cần thiết.
Stateless vs Stateful Services
Horizontal scaling chỉ hoạt động hiệu quả khi service là stateless — bất kỳ instance nào cũng xử lý được bất kỳ request nào.
Stateful (khó scale):
Client A ──► Server 1 (giữ session của A trong memory)
Client A ──► Server 2 ❌ Không có session!
Stateless (dễ scale):
Client A ──► Server 1 (đọc session từ Redis)
Client A ──► Server 2 (đọc session từ Redis) ✅Chiến lược chuyển từ stateful sang stateless:
- Session → External store (Redis, Memcached)
- File uploads → Object storage (S3, MinIO)
- In-memory cache → Distributed cache (Redis Cluster)
- Local cron jobs → Distributed job queue (Celery, Bull)
Database Scaling Strategies
Database thường là bottleneck đầu tiên khi hệ thống scale. Có 3 chiến lược chính:
1. Read Replicas — Tách read khỏi write
Hiệu quả khi hệ thống read-heavy (>80% reads). Replication lag thường 10-100ms — chấp nhận được cho hầu hết use case trừ financial transactions.
2. Sharding — Chia dữ liệu theo chiều ngang
| Sharding Strategy | Cơ chế | Ưu điểm | Nhược điểm |
|---|---|---|---|
| Range-based | user_id 1-1M → Shard A | Query range hiệu quả | Hotspot nếu phân bố không đều |
| Hash-based | hash(user_id) % N | Phân bố đều | Range query khó |
| Geo-based | Theo region/country | Latency thấp cho user | Cross-region queries phức tạp |
3. CDN — Đẩy content ra gần user
CDN giảm latency bằng cách cache static content (images, CSS, JS) tại edge nodes gần người dùng. Một CDN tốt giảm 50-80% load cho origin server và cải thiện đáng kể trải nghiệm người dùng ở vùng địa lý xa.
Auto-Scaling
Auto-scaling tự động thêm/bớt instance dựa trên metrics. Hai loại chính:
- Reactive: Scale khi CPU > 70% hoặc response time > 500ms. Đơn giản nhưng có lag 2-5 phút để instance mới sẵn sàng.
- Predictive: Dùng historical data để dự đoán traffic. AWS Auto Scaling hỗ trợ predictive scaling dựa trên pattern hàng tuần.
Metrics quan trọng cho auto-scaling:
| Metric | Ngưỡng scale-up | Ghi chú |
|---|---|---|
| CPU utilization | > 70% | Phổ biến nhất |
| Memory usage | > 80% | Quan trọng cho in-memory workloads |
| Request count | > N req/s/instance | Chính xác hơn CPU cho web services |
| Queue depth | > threshold | Tốt cho async workers |
| Response time P99 | > SLA target | User-facing metric trực tiếp |
Thực chiến
Tình huống: Capacity Planning cho e-commerce 10M users
Bối cảnh: Bạn đang thiết kế hệ thống e-commerce. Hiện tại 100K users, mục tiêu 10M users trong 2 năm. Flash sale có thể tạo spike 50x traffic bình thường.
Mục tiêu: Xác định infrastructure cần thiết và scaling strategy.
Bước 1: Ước lượng traffic
Daily Active Users (DAU): 10M × 20% = 2M
Requests/day: 2M × 10 requests/user = 20M
Requests/second (avg): 20M / 86400 ≈ 230 RPS
Peak (3x average): ~700 RPS
Flash sale spike (50x): ~11,500 RPSBước 2: Áp dụng quy tắc 10x
Quy tắc 10x nói rằng: thiết kế hệ thống để chịu được 10x traffic hiện tại mà không cần thay đổi kiến trúc. Nếu hiện tại 700 RPS peak, thiết kế cho 7,000 RPS. Flash sale 11,500 RPS nằm ngoài 10x → cần chiến lược riêng (queue, rate limiting, pre-scaling).
Bước 3: Xác định bottleneck theo layer
Phân tích:
- App layer: 7,000 RPS ÷ 500 RPS/instance = 14 instances (thêm buffer → 20 instances)
- Database: Bottleneck chính. 7,000 RPS mà cache hit rate 90% → 700 RPS đến DB. PostgreSQL single node xử lý được, nhưng flash sale cần read replicas + queue để throttle writes.
- Quyết định: Chưa cần shard database ở 10M users. Read replicas + caching layer là đủ.
Tình huống: Xác định scaling bottleneck
Bối cảnh: Response time P99 tăng từ 200ms lên 2s khi traffic tăng 3x.
Quy trình điều tra:
- Check metrics theo layer: CDN → LB → App → Cache → DB
- Tìm saturation point: Layer nào đạt >80% capacity trước?
- Phân tích: Nếu DB CPU 95% nhưng App CPU 30% → scale DB, không phải App
Trade-off đã chấp nhận: Dùng async replication cho read replicas, chấp nhận eventual consistency (lag ~100ms) để đổi lấy read throughput cao hơn.
Sai lầm điển hình
❌ Sai lầm 1: Premature Horizontal Scaling
Vấn đề: Team scale horizontally ngay từ đầu với 100 users.
Tại sao sai: Bạn đang trả giá complexity của distributed system (network latency, data consistency, deployment complexity) cho traffic mà một server trung bình xử lý dư sức. Một PostgreSQL instance trên máy 8-core xử lý được hàng nghìn TPS.
Đúng: Bắt đầu bằng single server mạnh. Monitor metrics. Scale khi có data chứng minh cần thiết, không phải khi "cảm thấy" sẽ cần.
❌ Sai lầm 2: Scale sai layer
Vấn đề: Response time chậm → Team thêm app servers. Nhưng root cause là database.
❌ SAI: Traffic tăng → thêm app server → DB vẫn là bottleneck → không cải thiện gì
✅ ĐÚNG: Traffic tăng → check metrics từng layer → DB CPU 95%
→ thêm read replicas + optimize queries → response time giảmTại sao hay mắc: Thêm app server dễ hơn (auto-scaling group), còn database scaling phức tạp hơn nên người ta trì hoãn. Kết quả: chi phí infrastructure tăng mà performance không cải thiện.
❌ Sai lầm 3: Bỏ qua database là bottleneck
Vấn đề: Mọi thứ đều stateless, auto-scale mượt mà — nhưng database vẫn là single instance.
Tại sao sai: Database là thành phần khó scale nhất trong stack. Nếu bạn scale mọi thứ trừ database, bạn chỉ đang chuyển bottleneck về database nhanh hơn. Giống như mở thêm làn đường nhưng tất cả đều đổ vào một trạm thu phí.
Đúng: Lập capacity plan cho database song song với app layer. Sớm đưa caching layer (Redis) vào giữa để giảm load cho DB. Target: >90% cache hit rate cho read-heavy workloads.
❌ Sai lầm 4: Scale không có metrics
Vấn đề: Scale dựa trên "cảm giác" hoặc complaint từ user.
Tại sao sai: Không có metrics nghĩa là không biết scale cái gì, bao nhiêu, và khi nào dừng. Bạn có thể đang scale đúng layer nhưng sai thời điểm, hoặc đúng thời điểm nhưng sai layer.
Đúng: Thiết lập monitoring stack (Prometheus + Grafana) ngay từ ngày đầu. Dashboard tối thiểu: CPU, Memory, Disk I/O, Network, Request Rate, Error Rate, Response Time (P50, P95, P99).
Under the Hood
Amdahl's Law — Giới hạn của Scaling
Amdahl's Law cho biết: nếu một phần P của chương trình có thể chạy song song (parallelized), speedup tối đa khi dùng N processors là:
Speedup(N) = 1 / ((1 - P) + P/N)Ý nghĩa thực tế:
| Phần parallelizable (P) | 2 cores | 8 cores | 64 cores | ∞ cores |
|---|---|---|---|---|
| 50% | 1.33x | 1.78x | 1.97x | 2x |
| 90% | 1.82x | 4.71x | 8.77x | 10x |
| 95% | 1.90x | 5.93x | 14.28x | 20x |
| 99% | 1.98x | 7.48x | 39.26x | 100x |
Dù thêm bao nhiêu server, phần serial (không parallelizable) luôn là ceiling. Nếu 10% code là serial → speedup tối đa chỉ 10x, dù bạn có 1000 servers.
Universal Scalability Law (USL)
USL mở rộng Amdahl's Law bằng cách thêm yếu tố coherency penalty (κ) — chi phí đồng bộ dữ liệu giữa các node:
C(N) = N / (1 + σ(N-1) + κN(N-1))
σ = contention (serial fraction)
κ = coherency (coordination cost giữa các node)Khi κ > 0: Throughput thực sự giảm sau một điểm nhất định. Thêm server lúc này làm hệ thống chậm hơn — vì coordination cost vượt quá benefit.
Đây là lý do tại sao distributed lock, distributed transaction, và shared state là những thứ đắt đỏ nhất trong hệ thống phân tán. Mỗi node thêm vào không chỉ cần communicate với tất cả node khác, mà còn phải chờ đồng bộ — tạo ra chi phí O(N²).
CAP Theorem Preview
Trong hệ thống phân tán, bạn chỉ có thể đảm bảo 2 trong 3 thuộc tính:
- Consistency — Mọi read đều nhận được write gần nhất
- Availability — Mọi request đều nhận được response (không lỗi)
- Partition Tolerance — Hệ thống vẫn hoạt động khi network bị phân mảnh
Trong thực tế, network partition luôn xảy ra, nên lựa chọn thực sự là CP vs AP:
| Chiến lược | Hy sinh | Use case |
|---|---|---|
| CP | Availability khi partition xảy ra | Banking, inventory, booking |
| AP | Consistency khi partition xảy ra | Social feed, analytics, DNS |
Trade-offs tổng hợp
| Quyết định | Khi NÊN | Khi KHÔNG NÊN |
|---|---|---|
| Vertical scaling | < 10K RPS, team nhỏ, cần đơn giản | Chi phí hardware vượt budget |
| Horizontal scaling | > 10K RPS, cần fault tolerance | < 1K RPS, tăng complexity không đáng |
| Sharding | > 1TB data hoặc write-heavy | Read-heavy system có thể dùng replicas |
| CDN | Static content, global users | Dynamic content, single region |
| Auto-scaling | Traffic biến động lớn | Traffic ổn định, có thể provision cố định |
Checklist ghi nhớ
✅ Checklist triển khai
Scaling Strategy
- [ ] Xác định bottleneck bằng metrics trước khi scale
- [ ] Bắt đầu vertical, chuyển horizontal khi có data chứng minh
- [ ] Thiết kế stateless services từ đầu
- [ ] Externalize tất cả state (session, cache, files) ra external store
Capacity Planning
- [ ] Ước lượng traffic: DAU → RPS → Peak → Spike
- [ ] Áp dụng quy tắc 10x cho current peak
- [ ] Lập capacity plan cho từng layer: CDN → LB → App → Cache → DB
- [ ] Database capacity plan riêng (thường là bottleneck đầu tiên)
Monitoring
- [ ] Dashboard: CPU, Memory, Disk I/O, Network, Request Rate, Error Rate
- [ ] Response time: P50, P95, P99
- [ ] Alert khi bất kỳ layer nào > 70% capacity
- [ ] Load test định kỳ để validate capacity plan
Bài tập luyện tập
Bài 1: Capacity Planning — Foundation
Đề bài: Một ứng dụng chat có 5M registered users, 500K DAU, mỗi user gửi trung bình 20 messages/ngày. Peak traffic gấp 5x average. Tính RPS cho message service.
🧠 Quiz
Câu hỏi: Với thông số trên, average RPS cho message service xấp xỉ bao nhiêu?
- [ ] A. 58 RPS
- [x] B. 116 RPS
- [ ] C. 580 RPS
- [ ] D. 1,160 RPS Giải thích: 500K × 20 = 10M messages/day. 10M / 86,400 ≈ 116 RPS average. Peak = 116 × 5 = 580 RPS. Option C là peak, không phải average.
💡 Gợi ý
- Tính tổng messages/day trước
- 1 ngày = 86,400 giây
- Phân biệt rõ average vs peak
Bài 2: Bottleneck Identification — Intermediate
Đề bài: Hệ thống e-commerce có metrics sau khi traffic tăng 2x:
- App server CPU: 45%
- Redis hit rate: 95%
- PostgreSQL CPU: 92%, connection pool: 98% utilized
- Response time P99: 3.2s (SLA: 500ms)
Xác định bottleneck và đề xuất 3 giải pháp theo thứ tự ưu tiên.
💡 Gợi ý
- Layer nào đang ở mức saturation (>80%)?
- Connection pool exhaustion gây gì?
- Short-term fix vs long-term fix
✅ Lời giải
Bottleneck: PostgreSQL — CPU 92% và connection pool 98% saturated.
Giải pháp theo ưu tiên:
- Ngay lập tức: Optimize slow queries (EXPLAIN ANALYZE), thêm missing indexes
- Ngắn hạn: Thêm read replicas, route SELECT queries sang replicas
- Trung hạn: Deploy connection pooler (PgBouncer), tăng pool capacity
Phân tích: App server chỉ 45% → không cần scale. Redis hit rate 95% → đang hoạt động hiệu quả. Database là single point of saturation.
Bài 3: Scaling Strategy Design — Advanced
Đề bài: Thiết kế scaling strategy cho video streaming platform với 50M users, 2M concurrent viewers peak, mỗi viewer consume trung bình 5 Mbps. Hệ thống hiện tại là monolith trên 3 servers.
Viết capacity plan bao gồm: bandwidth requirement, số lượng origin servers, CDN strategy, và database scaling approach.
💡 Gợi ý
- Bandwidth = concurrent viewers × bitrate
- CDN nên serve >95% video traffic
- Origin chỉ handle cache miss + upload
- Tách metadata DB ra khỏi video storage
✅ Lời giải
Bandwidth: 2M × 5 Mbps = 10 Tbps peak — không thể tự host, bắt buộc dùng CDN.
CDN Strategy: Target 98% cache hit → Origin chỉ handle 2% = 200 Gbps → khoảng 50 origin servers (4 Gbps each).
Database: Metadata (PostgreSQL) + Object Storage (S3) cho video files. Metadata DB: 50M users × 1KB = 50GB — single primary + read replicas đủ, sharding chưa cần thiết.
Auto-scaling: Transcoding workers scale theo upload queue depth, không theo CPU.
Liên kết học tiếp
Từ khóa glossary: Scalability, Horizontal Scaling, Vertical Scaling, Sharding, Read Replica, CDN, Auto-Scaling, Capacity Planning, Amdahl's Law, Universal Scalability Law, CAP Theorem
Tìm kiếm liên quan: thiết kế hệ thống chịu tải, mở rộng quy mô, database sharding, capacity estimation, quy tắc 10x