Hệ thống đang phục vụ 1,000 users, scale lên 50,000 — thay đổi gì?
Chiến lược scale từ 1K lên 50K users: load balancer, stateless, read replica, caching, async processing — từng bước giải quyết bottleneck thực tế.
Scale từ 1,000 lên 50,000 users
Câu hỏi
Hệ thống của bạn đang phục vụ 1,000 users. Bây giờ cần scale lên 50,000 users. Bạn thay đổi gì?
Dành cho level
Interviewer expect bạn biết không thể để 1 server xử lý mọi thứ nữa — cần load balancer, tách database riêng, và biết khái niệm horizontal scaling. Nói được "thêm server phía sau load balancer" và "tách database sang instance riêng" là đủ baseline.
Điểm cộng: biết stateless server là gì và tại sao session phải lưu vào Redis.
Cốt lõi cần nhớ
50x users không có nghĩa là 50x infrastructure. Phần lớn hệ thống ở mức 1K users chưa dùng hết capacity — một PostgreSQL instance xử lý thoải mái 5,000–10,000 QPS. Bước đầu tiên luôn là đo bottleneck thật, không phải thêm infra theo cảm tính.
Scale theo đúng thứ tự: vertical → horizontal app → database read replica → caching → async processing. Mỗi bước giải quyết một tầng bottleneck cụ thể. Nhảy bước (ví dụ sharding ở 50K users) là dấu hiệu over-engineering.
Stateless là tiền đề cho mọi horizontal scaling. Nếu server còn lưu session trong memory, thêm bao nhiêu server cũng vô nghĩa — user sẽ bị logout random khi request đến server khác.
Câu trả lời mẫu
"Trước hết tôi sẽ không thêm infra theo phản xạ mà bắt đầu bằng đo lường — xem bottleneck hiện tại ở đâu: CPU, memory, database connection pool, hay slow queries. Ở mức 1K users, nhiều khả năng hệ thống chưa dùng hết capacity, nên bước đầu tiên có thể chỉ đơn giản là vertical scaling — nâng spec server lên, thêm RAM, SSD nhanh hơn. Khi traffic thật sự quá tải cho 1 server, tôi chuyển sang horizontal scaling cho app tier: triển khai stateless server phía sau load balancer, chuyển session sang Redis. Tiếp theo nhìn database — ở 50K users thường read nhiều hơn write, nên tôi thêm read replica và route read traffic sang đó. Song song, tôi bật caching layer với Redis cho hot data — user profile, product listing, config — nhắm cache hit rate trên 90% để giảm 10x load cho database. Cuối cùng, những task nặng như gửi email, generate report, process image tôi đẩy sang message queue với Kafka hoặc Sidekiq, không để user chờ. Toàn bộ quá trình này tôi sẽ không đụng đến sharding hay microservices — ở 50K users đó là premature optimization. Instagram từng scale đến 30 triệu users mà vẫn trên PostgreSQL monolith với 3 engineers."
Phân tích chi tiết
Scaling Roadmap: 1K → 50K users
Bước 1: Vertical Scaling — giải pháp đầu tiên, rẻ nhất
Ở 1K users, server hiện tại thường dư capacity. Trước khi thay đổi architecture, nâng spec trước.
| Trước | Sau | Chi phí tăng |
|---|---|---|
| 2 vCPU, 4GB RAM | 8 vCPU, 32GB RAM | ~$100/tháng → ~$400/tháng |
| gp2 SSD | gp3 SSD (3000 IOPS) | +$20/tháng |
| db.t3.medium | db.r6g.xlarge | ~$200/tháng → ~$600/tháng |
Rule of thumb: Nếu CPU < 50%, memory < 60%, DB connections < 50% max → chưa cần horizontal scaling. Vertical scaling mua thời gian rất tốt.
Bước 2: Horizontal Scaling cho App Tier
Khi 1 server không đủ, thêm server phía sau load balancer. Điều kiện tiên quyết: app phải stateless.
Chuyển session sang Redis:
Kubernetes HPA — auto scale theo CPU:
Bước 3: Database — Read Replica + Query Optimization
Ở 50K users, database thường là bottleneck đầu tiên. 80-90% traffic là read → thêm read replica.
Spring Boot — route read/write:
Trước khi thêm replica — optimize query trước:
Bước 4: Caching Layer — giảm 10x database load
Spring Boot caching với Redis:
Cache gì, không cache gì:
| Nên cache | Không nên cache |
|---|---|
| User profile | Real-time balance/inventory |
| Product listing | Payment transactions |
| Config/feature flags | One-time tokens (OTP) |
| API response aggregate | Data thay đổi mỗi request |
| Session data | Security-sensitive data |
Target: cache hit rate ≥ 90%. Nếu chỉ 30% → kiểm tra TTL quá ngắn, cache key quá chi tiết (per-user thay vì shared), hoặc cache đang bị evict vì thiếu memory.
Bước 5: Async Processing — không để user chờ task nặng
Những thứ KHÔNG cần ở 50K users
| Không cần | Lý do |
|---|---|
| Database sharding | 50K users ≈ vài triệu rows — PostgreSQL xử lý thoải mái |
| Microservices | Tăng complexity 10x, team < 20 người không cần |
| Service mesh (Istio) | Over-engineering cho quy mô này |
| Multi-region | Trừ khi users phân tán toàn cầu |
| NoSQL migration | PostgreSQL + proper indexing đủ dùng |
| CQRS/Event sourcing | Trừ khi domain thực sự phức tạp |
Instagram case study: 30 triệu users, 3 engineers, PostgreSQL monolith, Django, không sharding. Họ chỉ thêm read replica + Redis + Memcached. Bài học: đừng giải quyết vấn đề bạn chưa có.
Monitoring — phát hiện bottleneck tiếp theo
Bẫy thường gặp
❌ "50K users thì phải chuyển sang microservices" → Tại sao sai: Microservices là giải pháp cho organizational scaling (50+ engineers), không phải traffic scaling. Một startup có bài học đau thương: tách monolith thành 12 microservices, response time từ 100ms lên 3 giây, error rate tăng 40x, AWS bill gấp 3. ✅ Đúng hơn: Monolith + horizontal scaling + caching xử lý được hàng triệu users trước khi cần microservices.
❌ "Sharding database ngay từ đầu để chuẩn bị cho tương lai" → Tại sao sai: Sharding là giải pháp cuối cùng, mang theo complexity khổng lồ — cross-shard query, distributed transaction, rebalancing. PostgreSQL single instance xử lý tốt tới hàng chục triệu rows. ✅ Đúng hơn: Optimize query + index → read replica → caching → vertical scaling DB. Sharding chỉ khi write volume thực sự quá tải.
❌ "Thêm server trước khi đo bottleneck"
→ Tại sao sai: Nếu bottleneck ở database, thêm 10 app server chỉ có nghĩa 10 server cùng chờ 1 database chậm. Giống mở thêm 10 làn đường trước một cây cầu 2 làn.
✅ Đúng hơn: Luôn đo trước — kubectl top, CloudWatch, pg_stat_statements. Tìm đúng tầng bottleneck rồi mới fix.
❌ "Session lưu trong server memory cũng được, dùng sticky session" → Tại sao sai: Sticky session (session affinity) phá vỡ load balancing, tạo hotspot, và khi server die → tất cả session mất. Twitter từng gặp vấn đề users bị logout random vì lý do này. ✅ Đúng hơn: Stateless server + shared session store (Redis/ElastiCache). Bất kỳ server nào cũng handle được bất kỳ request nào.
Câu hỏi follow-up
1. Ở 50K users, connection pool của database nên config bao nhiêu?
Rule of thumb:
connections = (core_count * 2) + effective_spindle_count. Với db.r6g.xlarge (4 vCPU): ~10 connections optimal per pool. Nếu có 5 app pods → tổng 50 connections. PostgreSQL default max_connections = 100 → vẫn thoải mái. Nếu cần nhiều hơn, dùng PgBouncer connection pooling thay vì tăng max_connections (mỗi connection tốn ~10MB RAM).
2. Cache invalidation xử lý như thế nào khi user update data?
Ba chiến lược: (1) TTL-based — set TTL 60s, chấp nhận stale data tối đa 60s, đơn giản nhất; (2) Write-through — khi write vào DB thì đồng thời update cache, consistent nhưng phức tạp hơn; (3) Cache-aside + evict on write — khi write thì xoá cache entry, request tiếp theo sẽ cache miss và rebuild. Phổ biến nhất là (3) — dùng
@CacheEvicttrong Spring Boot.
3. Nếu tiếp tục scale lên 500K users thì bước tiếp theo là gì?
CDN cho static assets (JS, CSS, images) → giảm bandwidth và latency. Database: thêm read replica thứ 2, partitioning bảng lớn (orders by month). Caching: nâng Redis lên cluster mode. Nếu team > 20 engineers → bắt đầu tách 1-2 bounded context thành service riêng (payment, notification) — nhưng vẫn không phải "full microservices".
4. Làm sao biết monolith đã đến giới hạn và cần tách service?
Hai tín hiệu chính: (1) Organizational — team > 30 engineers, merge conflict liên tục, deploy phải coordinate quá nhiều team; (2) Technical — có component cần scale khác biệt hoàn toàn (ví dụ image processing cần GPU mà API thì không). Nếu chưa có 2 tín hiệu này → monolith vẫn là lựa chọn đúng.