Hàng triệu phiếu bầu trong năm phút, không mất phiếu nào.

Go, Kafka, Redis và PostgreSQL giữ phiếu bầu chính xác và đẩy điểm số tới hàng triệu màn hình.

Top 5 từ Redis ZSET0 phiếu/s

Mô phỏng ZADD và ZREVRANGE với dữ liệu giả lập, không phải số liệu thật.

Không phải một ứng dụng CRUD

Yên tĩnh cả tuần, rồi hàng triệu request ập đến ngay khi MC mở cổng. Bốn ràng buộc quyết định toàn bộ kiến trúc.

Lưu lượng bùng nổ

Tăng giảm theo CPU là quá chậm. Hệ thống phải mở rộng theo hàng đợi, và tắt hẳn khi không có sự kiện.

nhiều ngày yên tĩnhMC mở cổng, 3 đến 5 phút

Đa khách thuê

Mỗi chiến dịch có thí sinh, luật điểm và subdomain mặc định dưới vinhcancode.vn. Về sau có thể gắn tên miền riêng, giống cách Shopify làm. Dữ liệu cuộc thi A không được lọt sang B.

sinhvienthanhlich-hvpn.vinhcancode.vnmissandmrsfa.vinhcancode.vntên miền riêng, giai đoạn sau

Lũy đẳng

Webhook VNPay, Momo, PayOS có thể bị gửi lại nhiều lần. Mỗi mã giao dịch chỉ xử lý đúng một lần.

x100 → x1

Bảng xếp hạng nhảy điểm tức thì

Không cần F5. Mỗi phiếu hợp lệ phải đẩy điểm mới tới hàng triệu kết nối WebSocket mà không cạn RAM máy chủ.

30-50 KBRAM mỗi kết nối với Centrifugo

Bốn thách thức, bốn chiến lược

Mỗi lựa chọn kiến trúc phía sau đều bắt nguồn từ một nút thắt cổ chai cụ thể.

Bão INSERT làm nghẽn cơ sở dữ liệuhàng đợi và ghi theo lô+

Vấn đề

Mỗi lệnh INSERT đi qua Parser, Planner, Executor rồi ghi WAL. Khi 100.000 người bấm trong cùng một giây, hồ chứa kết nối cạn kiệt và CPU chạm 100%.

Chiến lược

API chỉ ghi sự kiện vào hàng đợi rồi báo thành công ngay. Worker gom lô và nạp bằng COPY: 10.000 dòng trong 10 đến 30 ms, nhanh gấp hàng trăm lần INSERT.

ORDER BY vắt kiệt CPU khi xếp hạngcơ sở dữ liệu trên RAM+

Vấn đề

Mỗi lần mở bảng xếp hạng lại chạy ORDER BY score DESC LIMIT 100. Hàng triệu người xem cùng lúc sẽ làm sập máy chủ cơ sở dữ liệu.

Chiến lược

Đưa logic sắp xếp sang cơ sở dữ liệu in-memory với Sorted Set. Điểm cập nhật thẳng vào RAM, truy xuất Top 100 chưa tới 1 ms và không chạm vào cơ sở dữ liệu lõi.

Hàng triệu WebSocket ngốn cạn RAMproxy phân phối chuyên biệt+

Vấn đề

Bài toán C1M: một kết nối nhàn rỗi vẫn sinh gói ping/pong. Nếu API tự giữ 1 triệu kết nối, RAM tiêu tốn từ hàng chục tới hàng trăm GB.

Chiến lược

Tách hẳn một proxy Pub/Sub viết bằng Go, chỉ tốn 30 đến 50 KB RAM mỗi kết nối và gom nhịp tim. API gửi một tín hiệu, proxy fan-out xuống mọi màn hình.

Auto-scaling theo CPU quá chậmmở rộng theo sự kiện+

Vấn đề

Khi CPU chạm 80% thì hệ thống đã quá tải, hàng đợi nghẽn và request timeout. Chờ 2 đến 3 phút để khởi động máy mới là quá muộn.

Chiến lược

Hỏi hàng đợi thay vì hỏi CPU: còn bao nhiêu phiếu chưa xử lý (Consumer Lag). Worker nhân bản chủ động khi lag tăng và về 0 khi hàng đợi trống.

Bảy lớp, mỗi lớp một nhiệm vụ

Phiếu bầu ghi qua Kafka vào PostgreSQL. Điểm số tính trên Redis rồi đẩy xuống trình duyệt qua Centrifugo.

Luồng ghi: bảo vệ từng phiếu

Edgeingress-nginx và cert-manager cấp SSL cho subdomain mặc định của từng chiến dịch, và cho tên miền riêng khi tính năng này mở. Lua Script Sliding Window Log chạy nguyên tử trên Redis qua EVALSHA để chặn bot.
API (Go)Xác thực chữ ký HMAC_SHA256, khóa phân tán bằng SETNX. Trùng giao dịch vẫn trả 200 OK để cổng thanh toán ngừng gửi lại, rồi đẩy sự kiện vào Kafka.
Worker (Go)Gom lô 10.000 dòng, nạp bằng COPY nhị phân qua pgx. Nhanh gấp 100 đến 200 lần INSERT, khoảng 10 đến 30 ms mỗi lô.
PostgreSQLNguồn sự thật. Sổ cái kế toán kép, tổng Nợ và Có luôn bằng 0, cô lập tenant bằng Row-Level Security.

Luồng đọc: điểm số tới khán giả

Redis ClusterSorted Set là động cơ xếp hạng: ZADD khi có phiếu mới, ZREVRANGE lấy Top 100 trong micro-giây.
CentrifugoMỗi WebSocket tốn 30 đến 50 KB RAM. Bật client.batch_periodic_events và websocket.process_commands_off_read_loop cho hàng triệu kết nối nhàn rỗi.

Điều phối: mở rộng đúng lúc

KEDAScaledObject đo Consumer Lag của Kafka, nhân bản Worker khi lag vượt ngưỡng và về 0 Pod khi hàng đợi trống.
PrometheusKafka Exporter là mắt thần, cung cấp chỉ số để KEDA ra quyết định.

Sáu cái bẫy dù đã chọn đúng công nghệ

Công cụ mạnh vẫn có thể làm sập đêm chung kết nếu ghép sai. Chọn một bẫy để xem cách tránh.

AWS thay phần cứng, không thay kiến trúc

Worker Go vẫn phải gom lô, vẫn phải COPY, vẫn phải SET LOCAL. Cloud chỉ gánh việc vận hành hạ tầng.

PostgreSQL tự dựng→Amazon Aurora PostgreSQLTách compute và storage, chịu lỗi trên 3 vùng khả dụng, tự mở rộng tới 128 TB. pgx và COPY giữ nguyên.
Kafka tự dựng→Amazon MSKAWS lo broker và KRaft, nhưng không tránh được Hot Partition nên vẫn cần Key Salting.
Redis Cluster→Amazon ElastiCacheLua Script và Sorted Set chạy nguyên bản, không sửa dòng code nào.
NGINX Ingress + cert-manager→ALB + ACMChứng chỉ SSL miễn phí, tự gia hạn. Khi mở tên miền riêng, API gọi ACM mỗi lần ban tổ chức gắn tên miền mới.

Hai thành phần phải tự chạy trên Amazon EKS

KEDA: Auto Scaling mặc định đo CPU nên chỉ phản ứng khi đã quá tải. KEDA đọc Consumer Lag từ MSK và còn scale về 0.

Centrifugo: API Gateway WebSockets tính phí theo tin nhắn và số phút kết nối. Một triệu khán giả trong ba giờ có thể tốn hàng chục nghìn USD cho một đêm.

Đọc trực tiếp tài liệu gốc

Tám file markdown trong thư mục documents, mở ngay tại trang này bằng giao diện đọc riêng.