1. Vì sao Scalability quan trọng?
Scalability = khả năng hệ thống giữ performance khi load tăng. Một hệ scalable: load tăng 10× → throughput tăng gần 10×, latency không tăng đáng kể, chi phí tăng tuyến tính (không quadratic).
Hầu hết bug "khi prod chết" không phải bug logic — mà là bug scale. Code chạy đúng với 100 user, vỡ với 100k user vì:
- Query SELECT * không có index — 30s với bảng 100M dòng.
- 1 tab Excel mở 100MB JSON parse — RAM full.
- Mỗi request gọi external API — 100k QPS → bị throttle.
- 1 cron job lock cả bảng users — block website 5 phút.
1.1. Có 2 cách phát hiện vấn đề scale
- Reactive — production chết. Kinh khủng. Mất tiền và uy tín.
- Proactive — load test trước, profile, capacity planning. Đắt thời gian nhưng cứu mạng.
"Một thiết kế tốt là thiết kế hỏng theo cách dự đoán được. Một thiết kế tệ là thiết kế hỏng theo cách bất ngờ."
2. Vertical Scaling vs Horizontal Scaling
Vertical Scaling (Scale Up)
Mua server mạnh hơn — nhiều CPU core, nhiều RAM, NVMe nhanh hơn.
- Ưu: đơn giản, không sửa app, không phải nghĩ về CAP.
- Nhược: trần (server lớn nhất ~1TB RAM, 128 core), giá phi tuyến (server 2× mạnh có thể đắt 5×), SPOF (server chết = hệ chết).
- Khi dùng: app nhỏ-vừa, chưa đến giới hạn 1 server, ít khi cần HA tuyệt đối.
Horizontal Scaling (Scale Out)
Thêm nhiều server, chia tải qua load balancer.
- Ưu: scale gần vô hạn, HA tự nhiên (1 server chết, các server khác vẫn chạy), commodity hardware rẻ.
- Nhược: app phải stateless, complexity cao (LB, distributed cache, distributed transaction), CAP trade-off.
- Khi dùng: app cần scale lớn, downtime không chấp nhận được.
2.1. Quy tắc thực dụng
2.2. Stateless vs Stateful
Horizontal scaling đòi hỏi app stateless — không lưu data trong RAM của process. Vì sao?
// ❌ Stateful — lưu session trong RAM
const sessions = new Map();
app.post('/login', (req, res) => {
const sessionId = generateId();
sessions.set(sessionId, req.body.userId);
res.cookie('session', sessionId);
});
// User login → server A (sessions có entry)
// Request tiếp → load balancer route đến server B (KHÔNG có entry)
// → 401 Unauthorized 😱
Sửa: lưu session ở shared store (Redis, DB):
// ✓ Stateless — session trong Redis
app.post('/login', async (req, res) => {
const sessionId = generateId();
await redis.set(`session:${sessionId}`, req.body.userId, 'EX', 3600);
res.cookie('session', sessionId);
});
Component stateful (DB, Redis, file storage) chỉ ở 1 nơi hoặc có replication explicit. App tier hoàn toàn stateless.
3. Latency vs Throughput
3.1. Định nghĩa
- Latency — thời gian xử lý 1 request, đo bằng ms (mili-giây).
- Throughput — số request xử lý/giây, đo bằng QPS (queries per second), RPS (requests per second), TPS (transactions per second).
3.2. Mối quan hệ — Little's Law
L = λ × W:
- L = số request đang trong hệ (concurrency).
- λ = throughput (request/s).
- W = latency trung bình (s).
Ví dụ: app xử lý 100ms/request, có 50 worker → throughput tối đa = 50 / 0.1 = 500 QPS.
Để tăng throughput: hoặc giảm latency, hoặc tăng concurrency (worker, connection).
3.3. Tăng concurrency có thể TĂNG latency (queueing)
Khi worker bận hết, request mới phải chờ trong queue — latency = queue_time + processing_time. Đây là lý do tại sao "máy chủ overload" thường thấy latency tăng vọt 10-100×, không chỉ chậm tuyến tính.
Quy tắc: không bao giờ vận hành > 80% capacity. Để dư cho burst traffic và GC pause.
4. Percentile Metrics — P50, P95, P99
Latency trung bình (mean) là metric tệ — bị skew bởi outlier. Phân phối latency thường long-tail: đa số request nhanh, vài cái rất chậm.
4.1. Tại sao mean lừa
10 request: 9 cái 50ms, 1 cái 5000ms.
- Mean = (9×50 + 5000) / 10 = 545ms — sai lệch hoàn toàn.
- P50 = 50ms — phản ánh median user experience.
- P90 = 50ms — 90% nhanh.
- P99 hoặc max = 5000ms — đây mới là worst-case.
4.2. Định nghĩa
| Percentile | Ý nghĩa | Khi quan tâm |
|---|---|---|
| P50 (median) | 50% request nhanh hơn giá trị này | Average user experience |
| P90 | 90% nhanh hơn | Most users |
| P95 | 95% nhanh hơn | Standard SLO |
| P99 | 99% nhanh hơn | Worst-case acceptable; 1/100 user hit slowness |
| P99.9 | 99.9% nhanh hơn | SaaS lớn — 1/1000 = nhiều user |
4.3. Tail latency amplification
Vấn đề "P99 lan ra": nếu mỗi service có P99 = 10ms, request tổng đụng 10 service tuần tự → xác suất 1 service hit P99 ~ 10% — P99 tổng tăng phi tuyến tính.
Jeff Dean (Google) tóm tắt:
"Even the rare slow request becomes common when the request fans out to many services."
Cách giảm tail latency:
- Hedged requests: gửi request đến 2 replica, lấy cái về sớm hơn.
- Tied requests: gửi tới 1 replica, sau timeout T thì gửi thêm replica 2.
- Batching + concurrency: gọi parallel thay tuần tự.
4.4. SLA / SLO / SLI
- SLI (Service Level Indicator) — metric đo được: P99 latency, error rate, uptime.
- SLO (Service Level Objective) — target nội bộ: "P99 < 500ms 99.9% thời gian".
- SLA (Service Level Agreement) — cam kết với khách hàng có hậu quả pháp lý: "uptime > 99.9% hoặc refund".
SLO < SLA — luôn để buffer.
5. Availability — Nine Nines
| Availability | Downtime/năm | Downtime/tháng | Downtime/tuần |
|---|---|---|---|
| 99% (2 nines) | 3.65 ngày | 7.2 giờ | 1.68 giờ |
| 99.9% (3 nines) | 8.76 giờ | 43.8 phút | 10.1 phút |
| 99.95% | 4.38 giờ | 21.9 phút | 5 phút |
| 99.99% (4 nines) | 52.6 phút | 4.32 phút | 1.01 phút |
| 99.999% (5 nines) | 5.26 phút | 25.9 giây | 6 giây |
5.1. Compose availability
Hệ thống có nhiều component nối tiếp (sequential): availability tổng = tích availability từng cái.
- Frontend (99.9%) × Backend (99.9%) × DB (99.9%) = 99.7% — chỉ 2.6 nines!
- Mỗi component thêm giảm availability tổng.
Component song song (parallel, redundant): availability tổng = 1 - (1 - a)^n.
- 2 backend instance 99.9% → 1 - (0.001)² = 99.9999% — 6 nines!
- Đó là sức mạnh của redundancy.
5.2. Mỗi "9" thêm tốn 10×
Đi từ 99% → 99.9% có thể đơn giản (load balancer + 2 instance). 99.9% → 99.99% cần multi-AZ. 99.99% → 99.999% cần multi-region với replication phức tạp + automated failover dưới phút.
Cân nhắc thực tế: SaaS B2B thường target 99.9%. Banking/healthcare: 99.99%. Ít app cần 99.999% — chỉ telco / hệ thống critical.
5.3. Error budget
Nếu SLO = 99.9% (downtime 43 phút/tháng), bạn có "budget" 43 phút mỗi tháng để:
- Deploy có rủi ro (canary, A/B test).
- Maintenance.
- Migration.
Hết budget → freeze deploy đến hết tháng. Đây là cách Google SRE quản lý risk.
6. Reliability vs Availability vs Durability
| Khái niệm | Định nghĩa | Ví dụ metric |
|---|---|---|
| Reliability | Hệ thống hoạt động đúng (correct) liên tục | MTBF (mean time between failures) |
| Availability | % thời gian hệ thống có thể truy cập được | Uptime % (99.9%) |
| Durability | Data không bị mất qua thời gian/lỗi | S3: 11 nines (99.999999999%) |
Có thể có hệ availability cao nhưng reliability thấp (lúc nào cũng up, nhưng trả wrong data). Có hệ durability cao nhưng availability thấp (data an toàn nhưng đôi khi không truy cập được).
Thiết kế tốt cân bằng cả 3.
7. Bottleneck Analysis & Universal Scalability Law
7.1. Bottleneck — định nghĩa
Bottleneck = thành phần chậm nhất / capacity nhỏ nhất quyết định throughput tổng. Không quan trọng các phần khác mạnh mức nào, hệ chạy bằng tốc độ bottleneck.
Phép ẩn dụ: ống dẫn nước với nhiều đoạn — đoạn hẹp nhất quyết định flow rate.
7.2. Phương pháp tìm bottleneck
- Đo CPU, RAM, disk I/O, network của mỗi component.
- Cái nào đầu tiên đạt 80%+ utilization → đó là bottleneck.
- Tăng capacity của bottleneck — không có ý nghĩa tăng phần khác.
- Sau khi fix, bottleneck dịch chuyển sang chỗ khác. Lặp lại.
7.3. Amdahl's Law
Speedup tối đa khi parallelize: S = 1 / ((1-p) + p/n), với p = phần parallelize được, n = số core.
Nếu 90% code parallelize được, dùng 100 core: S = 1 / (0.1 + 0.9/100) = 9.17×. KHÔNG phải 100×.
Hệ quả: phần tuần tự (serial) là tường giới hạn. Đó là lý do "scale ngang vô hạn" trong thực tế bị giới hạn bởi phần coordination, sync, lock.
7.4. Universal Scalability Law (USL) — Neil Gunther
Mở rộng Amdahl's bằng cách thêm coherency cost — overhead cross-coordination tăng quadratic với số node:
C(N) = N / (1 + α(N-1) + βN(N-1))
- α = contention (queueing).
- β = coherency (synchronize between nodes).
Khi N tăng đủ lớn, βN² thắng → throughput thực ra giảm. Đây là lý do thêm node có thể không tăng throughput mà còn giảm.
7.5. Bài học thực tế
- Không phải lúc nào "thêm máy" = "nhanh hơn".
- Distributed system có overhead — đôi khi 1 node mạnh nhanh hơn 10 node yếu.
- Đo trước, scale sau.
8. Capacity Planning — Back-of-the-envelope Math
Kỹ năng phỏng vấn quan trọng: tính nhanh requirement của hệ. Quy trình 5 bước:
- DAU — daily active users. Nhân với 5-10 = peak concurrent.
- Mỗi user làm gì — read/write/upload/search QPS.
- Tổng QPS — DAU × actions/user / 86400 (giây/ngày). Peak = 3-5× average.
- Storage — size mỗi record × số record × growth.
- Bandwidth — request size × QPS.
8.1. Ví dụ: Twitter
- 500M users, 20% DAU = 100M.
- Mỗi user post 1 tweet, đọc 100 tweet → 100M write/day, 10B read/day.
- Write QPS avg: 100M/86400 ≈ 1160 → peak ~5000.
- Read QPS avg: 10B/86400 ≈ 116k → peak ~580k.
- 1 tweet ~ 280 char + metadata = 1KB → 100M × 1KB = 100GB/day, 36TB/year.
- Hot data (recent 30 days) = 3TB → fit RAM cluster cache.
8.2. Số liệu nên thuộc
| Operation | Latency |
|---|---|
| L1 cache reference | 1 ns |
| L2 cache reference | 4 ns |
| RAM access | 100 ns |
| Mutex lock/unlock | 17 ns |
| Compress 1KB with Snappy | 2 µs |
| SSD random read 4KB | 50 µs |
| Read 1MB sequentially from RAM | 250 µs |
| Read 1MB sequentially from SSD | 1 ms |
| Network roundtrip same DC | 0.5 ms |
| Read 1MB sequentially from HDD | 20 ms |
| Roundtrip US east ↔ US west | ~70 ms |
| Roundtrip US ↔ EU | ~100 ms |
| Roundtrip US ↔ APAC | ~150 ms |
Nguồn: Jeff Dean (Google) — "Numbers Every Programmer Should Know".
8.3. Common QPS / Storage benchmark
- 1 Postgres node tốt: 50k QPS read, 10-20k QPS write.
- 1 Redis node: 100k+ QPS với latency < 1ms.
- 1 Kafka broker: 1M messages/s.
- nginx 1 node: 50k+ HTTP/s.
- 1 modern CPU core: ~10k function calls/ms (1ns mỗi cái).
- 1 server modern: 100GB+ RAM, 32-64 core, hàng TB SSD.
8.4. Quy tắc đơn vị nhanh
2^10 ≈ 10^3 (1KB ≈ 1000)
2^20 ≈ 10^6 (1MB ≈ 1M)
2^30 ≈ 10^9 (1GB ≈ 1B)
2^40 ≈ 10^12 (1TB ≈ 1T)
86400 s/day ≈ 10^5
86400 × 30 ≈ 2.6 × 10^6 (1 month)
86400 × 365 ≈ 3.15 × 10^7 (1 year)
9. Load Testing — đo trước khi vỡ
9.1. Các loại test
- Load test — load expected, đo latency/error stable.
- Stress test — load tăng dần đến khi vỡ → tìm capacity limit.
- Spike test — load tăng đột ngột → kiểm tra autoscale.
- Endurance test — load steady trong vài giờ-ngày → tìm memory leak, GC issue.
- Capacity planning — đo từng config khác nhau, project khi nào hết capacity.
9.2. Tools
- k6 — JS scripting, modern, dễ dùng.
- Locust — Python, distributed.
- JMeter — Java, GUI, classic.
- wrk / wrk2 — đơn giản, throughput cao.
- vegeta — Go, command-line.
9.3. Ví dụ k6
// k6-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // ramp up
{ duration: '5m', target: 100 }, // stay
{ duration: '2m', target: 200 }, // increase
{ duration: '5m', target: 200 },
{ duration: '2m', target: 0 }, // ramp down
],
thresholds: {
'http_req_duration{status:200}': ['p(95)<500'], // P95 < 500ms
'http_req_failed': ['rate<0.01'], // <1% errors
},
};
export default function () {
const res = http.get('https://api.example.com/users/1');
check(res, { 'status 200': (r) => r.status === 200 });
sleep(1);
}
// Run:
// k6 run k6-test.js
9.4. Best practice
- Test trên environment giống prod (hardware, config).
- Đừng test trên localhost — không meaningful.
- Đo nhiều metric cùng lúc: latency P50/P95/P99, error rate, CPU/RAM/IO.
- Lặp lại test nhiều lần — variance lớn.
- Cẩn thận với rate limit — đừng tự DDOS production.
10. Bài tập
- Tính capacity cho ứng dụng đặt vé phim:
- 10M user, 1M DAU, peak hour = 8-10pm.
- Mỗi user xem 5 phim/tháng, mỗi phim 30 lần search trước khi đặt.
- Mỗi search trả 100 movie, mỗi movie 5KB.
- Tính: read QPS, write QPS, storage/year, bandwidth.
- So sánh availability:
- Hệ A: 1 server 99.9%.
- Hệ B: 2 server song song mỗi cái 99.9%.
- Hệ C: 3 server tuần tự (LB → app → DB) mỗi cái 99.9%.
- Tính availability mỗi hệ.
- Latency P99 = 1000ms, P50 = 50ms. Vấn đề gì? Cách tìm root cause?
- Setup k6 local. Test 1 endpoint của project bạn. Tìm capacity limit của 1 node.
- Tính: 100k user concurrent, mỗi user xem video 1080p (5 Mbps). Tổng bandwidth bao nhiêu? Cần CDN không?
- Khi nào nên scale vertical thay horizontal? Cho 2 ví dụ thực tế.
- Theo USL, vì sao thêm node có thể không tăng (thậm chí giảm) throughput?
11. Quiz
Quiz cuối Chương 1
Vì sao mean (average) latency là metric tệ?
Horizontal scaling đòi hỏi app:
99.9% availability tương đương downtime/tháng:
Hệ có 3 component nối tiếp 99.9% mỗi cái → availability tổng:
Little's Law L = λ × W:
"Tail latency amplification" (Jeff Dean):
"Universal Scalability Law" (USL) cho thấy:
Quy tắc thực dụng vận hành server:
Hoàn thành Chương 1. Tiếp theo: Chương 2 — Load Balancer & Reverse Proxy →