Case study load balancing này ghi lại cách chúng tôi giúp một website e-commerce chịu được 100.000 concurrent users trong đợt flash sale — từ kiến trúc chỉ chịu được 5.000 users. Không downtime, không mất đơn hàng, và ROI gấp 15 lần chi phí đầu tư.
Bối cảnh
Khách hàng: Sàn TMĐT thời trang, thị trường Việt Nam.
Vấn đề: Mỗi đợt flash sale (1 lần/tháng), website sập trong 5 phút đầu tiên. Lần gần nhất (flash sale 11/11):
- 70.000 users truy cập đồng thời
- Website sập hoàn toàn lúc 0h02
- Mất 3 giờ để khôi phục
- Thiệt hại ước tính: 1.2 tỉ VND doanh thu + mất uy tín
Hệ thống cũ:
- 1 VPS (8GB RAM, 4 vCPU)
- Nginx + PHP-FPM + MySQL trên cùng 1 server
- Không CDN, không caching layer
- Capacity: ~5.000 concurrent users
Yêu cầu: Xây dựng hệ thống chịu 100.000 concurrent users cho đợt flash sale 12/12, trong vòng 3 tuần.
Thiết kế kiến trúc mới
Nguyên tắc thiết kế
- Scale horizontally: Thêm server thay vì nâng cấp server
- Stateless application: Không lưu state trên app server
- Cache everything possible: Giảm tải database
- Graceful degradation: Nếu quá tải, giảm tính năng thay vì sập hoàn toàn
Kiến trúc tổng thể
Users
↓
Cloudflare CDN (300+ PoPs)
↓ (DDoS protection + static cache)
Load Balancer (HAProxy)
├── App Server 1 (Nginx + PHP-FPM)
├── App Server 2 (Nginx + PHP-FPM)
├── App Server 3 (Nginx + PHP-FPM)
└── App Server 4 (chỉ bật khi flash sale)
↓
Redis Cluster (Session + Cache)
↓
MySQL Primary → MySQL Replica (read)
Chi tiết từng tầng
Tầng 1: Cloudflare CDN + WAF
| Chức năng | Cấu hình |
|---|---|
| Cache static files | Ảnh, CSS, JS, fonts — TTL 30 ngày |
| Cache HTML | Trang chủ, category — TTL 5 phút |
| DDoS protection | Auto, sensitivity: Medium |
| Rate limiting | 100 requests/phút cho /checkout |
| WAF | Block SQL injection, XSS |
| Waiting Room | Kích hoạt khi > 80.000 concurrent |
Cloudflare Waiting Room: Tính năng “phòng chờ” — khi traffic vượt ngưỡng, users mới phải xếp hàng, không làm sập server.
Tầng 2: HAProxy Load Balancer
# /etc/haproxy/haproxy.cfg
frontend http-in
bind *:80
bind *:443 ssl crt /etc/ssl/cert.pem
default_backend app_servers
backend app_servers
balance roundrobin
option httpchk GET /health
server app1 10.0.0.1:80 check weight 100
server app2 10.0.0.2:80 check weight 100
server app3 10.0.0.3:80 check weight 100
server app4 10.0.0.4:80 check weight 50 # Flash sale only
| Thuật toán | Mô tả | Use case |
|---|---|---|
| Round Robin | Chia đều request | Default, app servers cùng cấu hình |
| Least Connections | Gửi đến server ít kết nối nhất | Khi requests có thời gian xử lý khác nhau |
| IP Hash | Cùng IP luôn đến cùng server | Session sticky (không cần nếu có Redis) |
| Weighted | Server mạnh nhận nhiều hơn | App servers khác cấu hình |
Tầng 3: Application Servers
Mỗi app server (4GB RAM, 2 vCPU):
- Nginx + PHP 8.3 FPM
- Stateless — session lưu trên Redis, không lưu local
- OPcache enabled
- PHP worker processes: 50
Tầng 4: Redis Cluster
- Redis 1 (4GB RAM): Session storage + Object cache
- Redis 2 (2GB RAM): Queue (đơn hàng chờ xử lý)
Flash sale optimization:
# Pre-warm cache trước 1 giờ
# Cache tất cả sản phẩm flash sale vào Redis
redis-cli SET product:1234 '{"name":"...", "price":199000, "stock":500}'
redis-cli SET product:1234:stock 500 # Atomic counter cho tồn kho
Tầng 5: Database
- MySQL Primary (16GB RAM, 8 vCPU): Write operations
- MySQL Replica (8GB RAM, 4 vCPU): Read operations (listing, search)
- Read/Write split qua application code
Chiến lược flash sale
Pre-sale (trước 24 giờ)
- Bật app server 4 (reserve server)
- Pre-warm cache: Load tất cả sản phẩm flash sale vào Redis
- Pre-generate static pages: Trang chủ, landing page flash sale
- Scale database connections: Tăng max_connections
- Alert team: Monitoring heightened alerting
During sale (0h00 – 2h00)
- Cloudflare Waiting Room kích hoạt khi > 80.000 concurrent
- Tồn kho quản lý qua Redis (atomic decrement, không query MySQL cho mỗi đơn)
- Order queue: Đơn hàng đẩy vào Redis queue, xử lý async
- Disable non-essential features: Tắt related products, reviews, analytics tracking
Post-sale
- Tắt app server 4 (tiết kiệm chi phí)
- Process order queue (đơn hàng chờ trong Redis)
- Sync inventory (Redis → MySQL)
- Generate reports
Kết quả flash sale 12/12
Traffic
| Metric | Flash sale cũ (11/11) | Flash sale mới (12/12) |
|---|---|---|
| Peak concurrent users | 70.000 (sập) | 105.000 |
| Requests/giây (peak) | 15.000 (sập) | 45.000 |
| Uptime | 0% (3 giờ sập) | 100% |
| Avg response time (peak) | N/A (sập) | 280ms |
| Error rate | 100% | 0.02% |
Business
| Metric | 11/11 (cũ) | 12/12 (mới) | Thay đổi |
|---|---|---|---|
| Revenue (đêm flash sale) | ~200M VND (trước khi sập) | 2.8 tỉ VND | +1300% |
| Đơn hàng | 2.500 (trước khi sập) | 18.500 | +640% |
| Conversion rate | N/A | 4.2% | — |
| Customer complaints | 500+ | 12 | -98% |
Chi phí hạ tầng
| Hạng mục | Hàng tháng (thường) | Flash sale (thêm) |
|---|---|---|
| 3 App servers (4GB) | 3.6 triệu VND | — |
| 1 App server (reserve) | 0 | 200.000 VND (2 ngày) |
| Load balancer (HAProxy) | 600.000 VND | — |
| Redis cluster (2 instances) | 1.2 triệu VND | — |
| MySQL Primary | 2.4 triệu VND | — |
| MySQL Replica | 1.2 triệu VND | — |
| Cloudflare Pro | 500.000 VND | — |
| Tổng | 9.5 triệu VND/tháng | +200.000 VND |
So sánh:
- Hệ thống cũ: 1.5 triệu VND/tháng + thiệt hại 1.2 tỉ VND/flash sale
- Hệ thống mới: 9.5 triệu VND/tháng + doanh thu 2.8 tỉ VND/flash sale
- ROI: 295x chi phí tăng thêm
Bài học kinh nghiệm
1. Đừng đợi sập mới scale
Lên kế hoạch capacity trước 2–4 tuần. Load test trước flash sale ít nhất 1 tuần. Chi phí chuẩn bị trước luôn rẻ hơn chi phí khắc phục sự cố.
2. Stateless application là bắt buộc
Nếu session lưu trên app server, không thể load balance. Chuyển session sang Redis/Memcached để bất kỳ server nào cũng xử lý được bất kỳ request nào.
3. Cache là vũ khí số 1
Database là bottleneck #1 khi traffic tăng. Cache 90% read operations vào Redis → database chỉ xử lý writes → chịu được traffic gấp 10x.
4. Graceful degradation > crash
Khi quá tải, tắt features không quan trọng (reviews, related products, analytics) thay vì để website sập hoàn toàn. Waiting room cho phép kiểm soát traffic thay vì bị overwhelm.
5. Monitor real-time trong sự kiện
Trong flash sale, cần dashboard real-time hiển thị:
- Concurrent users, requests/giây
- Response time, error rate
- CPU, RAM, disk I/O mỗi server
- Redis memory, hit rate
- MySQL connections, slow queries
- Order count, revenue
FAQ — Câu hỏi thường gặp
Load balancing có cần cho website nhỏ không?
Chưa cần nếu traffic < 10.000 visitors/ngày. Nhưng nên thiết kế stateless từ đầu (session trên Redis) để dễ dàng thêm load balancing khi cần. Chi phí setup stateless từ đầu: gần 0. Chi phí refactor sau: 10–30 triệu VND.
HAProxy vs Nginx Load Balancer — dùng cái nào?
Cả hai đều tốt. HAProxy chuyên về load balancing, có health checks tốt hơn. Nginx đa dụng hơn (vừa web server vừa load balancer). Nếu đã dùng Nginx: dùng Nginx upstream. Nếu cần load balancer riêng: HAProxy. Hoặc dùng Cloudflare Load Balancing (managed, không cần tự quản lý).
Kết luận
100.000 concurrent users không phải con số “enterprise only” — với kiến trúc đúng (CDN + Load Balancer + Redis + DB replica), chi phí chỉ ~10 triệu VND/tháng. Quan trọng nhất là thiết kế kiến trúc đúng từ đầu và chuẩn bị trước cho peak traffic.
Nếu bạn đang lên kế hoạch flash sale hoặc cần xây dựng hệ thống có khả năng scale cho doanh nghiệp, hãy liên hệ Trinh Digital để được tư vấn kiến trúc phù hợp.