SU02 · Phát triển Phần mềm Số 02 · ISC · FPT Telecom

Performance & Scale Handbook

Xây trực giác về bậc độ lớn trước khi bàn tới pattern. Bộ số phải thuộc, napkin math, thang bậc mở rộng 7 bậc, bốn hiện tượng cốt lõi và checklist rà soát thiết kế.

Tài liệu 04 / 10Trụ năng lực 02Đối tượng: toàn bộ Dev · TL · SABản v1.0
← Bộ tài liệu SA · SU02
Nội dung
  1. Vì sao đây là điểm yếu chung
  2. Performance khác Scale
  3. Bộ số phải thuộc lòng
  4. Napkin math — quy trình 5 bước
  5. Thang bậc mở rộng — 7 bậc
  6. Bốn hiện tượng phải hiểu bằng trực giác
  7. Đọc và diễn giải chỉ số
  8. Ba bài toán scale của SU02
  9. Checklist rà soát thiết kế
  10. Anti-patterns về scale

01Vì sao đây là điểm yếu chung — và cách chữa

Phần lớn kỹ sư phần mềm tại Việt Nam ít có cơ hội chạm hệ thống thật sự lớn. Hệ quả là kiến thức về performance và scale bị học lý thuyết: thuộc tên pattern nhưng không có trực giác về bậc độ lớn. Biểu hiện điển hình:

Cách chữa không phải học thêm pattern, mà là xây trực giác về con số. Một kỹ sư ước lượng được bậc độ lớn trong 5 phút trên giấy sẽ ra quyết định tốt hơn một kỹ sư thuộc 30 pattern nhưng không biết hệ thống của mình đang ở đâu trên thang tải.

01APerformance khác Scale như thế nào?

Hai khái niệm liên quan nhưng không đồng nghĩa:

Khái niệmCâu hỏi chínhDấu hiệu điển hình
PerformanceHệ thống xử lý một request nhanh và ổn định đến mức nào?Latency cao, query chậm, N+1 query, serialization thừa, thiếu index.
ScaleHệ thống có giữ được chất lượng khi tải tăng không?Latency tăng phi tuyến, queue phình, database chạm trần, thiếu capacity.
Ví dụ: Một query mất 2 giây dù chỉ có 100 người dùng là vấn đề performance. Query mất 100 ms hôm nay nhưng tăng lên 5 giây khi traffic tăng 20 lần là vấn đề scale. Một hệ thống có thể nhanh nhưng không scale, hoặc scale được nhưng vẫn chậm.

Trình tự xử lý: đo và sửa bottleneck hiện tại trước, sau đó mới tính cách hấp thụ tải tăng. Không dùng microservices, cache hoặc sharding để che một lỗi performance chưa được profile.

02Bộ số phải thuộc lòng

Đây là phần cụ thể nhất, dễ dạy nhất và tạo khác biệt lớn nhất. Yêu cầu cả cohort thuộc, kiểm tra miệng đầu mỗi buổi Dojo. Không cần chính xác — cần đúng bậc độ lớn.

Độ trễ

Thao tácBậc độ lớnGhi nhớ
Truy cập L1 cache~1 nsTrong máy: nano tới micro
Truy cập bộ nhớ chính (RAM)~100 ns
Đọc tuần tự 1 MB từ RAM~10 µs
SSD random read~100 µsChậm hơn RAM ~1000×
Đọc tuần tự 1 MB từ SSD~1 msTuần tự luôn rẻ hơn ngẫu nhiên
Round-trip mạng trong cùng datacenter~0.5 msCon số quan trọng nhất
Query Postgres có index, dữ liệu trong cache0.1 – 1 msKhông index: gấp 100–10.000×
Round-trip Hà Nội ↔ TP.HCM~30 msÁnh sáng là trần, không tối ưu được
Round-trip Việt Nam ↔ Singapore~40 msẢnh hưởng lớn khi dùng cloud region ngoài
Round-trip Việt Nam ↔ US East~230 msMỗi lần gọi API xuyên lục địa
Ngưỡng người dùng cảm thấy "tức thì"< 100 msTrên 1 s là mất mạch chú ý

Dung lượng & thông lượng

Thành phầnBậc độ lớn tham chiếu
Một instance Postgres cấu hình khá — write đơn giảnvài nghìn/s
Một instance Postgres — read có index, cache tốthàng chục nghìn/s
Một Kafka brokerhàng trăm nghìn msg/s
Một Redis instance~100 nghìn ops/s
Một node ứng dụng (Java/Node), không chạm DBvài nghìn req/s
Một node ứng dụng, mỗi request 2–3 query DBvài trăm req/s
1 triệu bản ghi × 1 KB~1 GB
Cách dùng bảng này khi thiết kế: tính ra con số yêu cầu, so với bảng, rồi hỏi "cách hiện tại còn cách trần bao xa". Nếu còn cách 10× thì không cần kiến trúc mới. Nếu vượt trần thì mới bàn tới sharding hay tách service.

03Napkin math — quy trình 5 bước

Mọi bài toán trong Architecture Dojo đều bắt đầu bằng 5 phút này, làm trên giấy, không máy tính.

#BướcCách làm
1Quy về đơn vị giây1 ngày ≈ 86.400 s, làm tròn 100.000 s cho dễ nhẩm. 1 triệu/ngày ≈ 10/s. 5 triệu/ngày ≈ 50–60/s.
2Nhân hệ số đỉnhTải đỉnh thường gấp 5–10× trung bình. Với chiến dịch marketing hoặc khung giờ khuyến mãi có thể 50–100×. Luôn hỏi: đỉnh dồn trong bao lâu?
3Tính dung lượngSố bản ghi/ngày × kích thước bản ghi × số ngày lưu. Nhân 2–3× cho index và replica.
4Tính băng thôngTPS × payload. Chú ý cả chiều ra (fan-out tới các service khác thường lớn hơn chiều vào).
5So với trần thành phầnĐối chiếu bảng §02. Xác định thành phần nào chạm trần trước — đó là chỗ cần thiết kế, phần còn lại để yên.

Ví dụ mẫu — campaign engine FMI

Yêu cầu: 5 triệu thông báo cá nhân hoá/ngày, đỉnh dồn trong 30 phút

Bước 1  5.000.000 / 100.000 s        ≈ 50 msg/s trung bình
Bước 2  Đỉnh: 60% lượng dồn 30 phút
        3.000.000 / 1.800 s          ≈ 1.700 msg/s  ← con số thiết kế
        (gấp 34× trung bình — thiết kế theo 50/s là sai hoàn toàn)
Bước 3  5 triệu × 500 B × 90 ngày    ≈ 225 GB  → ×3 (index+replica) ≈ 675 GB
Bước 4  1.700/s × 500 B              ≈ 850 KB/s ra ngoài — không đáng lo
Bước 5  1.700 write/s vào Postgres   → CHẠM TRẦN một instance
        1.700 msg/s vào Kafka        → thoải mái (dưới 1% năng lực)

Kết luận: nút thắt nằm ở tầng ghi dữ liệu, không phải tầng vận chuyển.
          Thiết kế phải tập trung vào batching / async write, không phải
          vào việc chọn message broker nào.
Điểm dạy quan trọng nhất của ví dụ này: phần lớn người mới sẽ dành 80% thời gian tranh luận Kafka vs RabbitMQ — trong khi phép tính cho thấy đó không phải nút thắt. Napkin math giúp đặt công sức thiết kế đúng chỗ.

04Thang bậc mở rộng — 7 bậc, không nhảy cóc

Lỗi phổ biến nhất: nghe "scale" là nhảy thẳng lên bậc 5–6. Dạy và áp dụng đúng thứ tự.

BậcBiện phápKhi nào dùng · cái giá phải trả
0Sửa cái đang saiIndex thiếu, N+1 query, thiếu connection pool, gọi API tuần tự thay vì song song, serialize thừa. Phần lớn "vấn đề performance" dừng ở đây. Giá: gần bằng 0. Nguyên tắc: chưa profile thì chưa được đề xuất kiến trúc mới.
1Scale dọcTăng CPU/RAM/IOPS. Rẻ, nhanh, không thêm phức tạp. Bị đánh giá thấp một cách oan uổng. Giá: tiền, và có trần cứng.
2Tách đọc / ghiRead replica, CQRS nhẹ. Giải quyết phần lớn hệ thống có tỉ lệ đọc cao. Giá: replication lag → phải chấp nhận đọc dữ liệu cũ vài trăm ms.
3CacheGiảm tải đọc mạnh nhất với chi phí thấp nhất. Giá: ba vấn đề khó — invalidation, cache stampede khi cache chết, và cache trở thành single point of failure.
4Async & queueTách việc chậm khỏi luồng đồng bộ, hấp thụ tải đỉnh. Giá: phải hiểu idempotency, ordering, DLQ, và người dùng phải chấp nhận kết quả không tức thì.
5Sharding / partitionChỉ khi thật cần. Giá: chọn partition key là quyết định khó đảo ngược nhất trong kiến trúc dữ liệu — sai thì phải migrate toàn bộ. Mất khả năng join và transaction xuyên shard.
6Tách service theo trục scaleTách phần cần scale khác nhau ra riêng — không tách theo sơ đồ tổ chức. Giá: độ phức tạp vận hành, tail latency amplification, phải có năng lực observability tương xứng.
Quy tắc bắt buộc trong Dojo: khi trình bày, nếu đề xuất bậc ≥ 4, người trình bày phải giải thích được vì sao bậc 0–3 không đủ. Không giải thích được thì thiết kế bị trả về, bất kể vẽ đẹp thế nào.

05Bốn hiện tượng phải hiểu bằng trực giác

① Hot partition — chia đều trên lý thuyết, lệch trong thực tế

Dữ liệu thực tế gần như luôn tuân theo phân phối lệch. Trong B2B, khoảng 10% khách hàng lớn tạo ra 70–80% giao dịch. Partition theo customer_id nghe hợp lý, nhưng shard chứa khách hàng lớn nhất sẽ chết trước trong khi các shard khác nhàn rỗi.

Cách nhận diện: hỏi "10 giá trị phổ biến nhất của partition key chiếm bao nhiêu phần trăm dữ liệu?" Nếu trên 30% thì đã có vấn đề.

Cách xử lý: composite key (customer_id + thời gian hoặc + hash bucket) · tách riêng nhóm khách hàng lớn · hoặc chọn key khác hoàn toàn.

② Tail latency amplification — chia nhỏ service không miễn phí

Một request gọi song song 10 service, mỗi service có p99 = 100 ms. Xác suất ít nhất một service rơi vào p99 là 1 − 0.99¹⁰ ≈ 9,6%. Nghĩa là gần 10% request tổng sẽ chậm ít nhất 100 ms — dù mỗi service đều "khoẻ".

Hệ quả kiến trúc: càng fan-out rộng, p99 của hệ thống càng xấu theo cấp số. Đây là lý do kỹ thuật khiến "cứ tách nhỏ service ra" là lời khuyên nguy hiểm khi chưa có hedged request, timeout hợp lý và khả năng trả kết quả một phần.

③ Cascading failure — hệ thống chết dây chuyền

Chuỗi nhân quả điển hình: một service downstream chậm → caller giữ connection lâu hơn → connection pool cạn → caller ngừng phục vụ mọi request kể cả những request không liên quan → service gọi caller cũng chết theo → toàn hệ thống sập trong vài phút.

Biện phápTác dụng
TimeoutBắt buộc, ở mọi lời gọi ra ngoài. Không có timeout là lỗi thiết kế, không phải thiếu sót nhỏ.
Circuit breakerNgừng gọi service đang chết, trả lỗi nhanh thay vì chờ. Cho downstream cơ hội hồi phục.
BulkheadTách pool tài nguyên theo nhóm chức năng — một phần chết không kéo cả tàu chìm.
Load sheddingChủ động từ chối bớt request khi quá tải, ưu tiên request quan trọng.
Retry có jitterRetry không jitter tạo ra thundering herd — làm hệ thống chết nhanh hơn.
Bài học cốt lõi: từ chối bớt yêu cầu tốt hơn là chết cả hệ thống. Phần lớn thiết kế của kỹ sư ít kinh nghiệm không có khái niệm "từ chối có kiểm soát".

④ Chi phí phối hợp — Universal Scalability Law

Thêm node không làm throughput tăng tuyến tính, vì hai lý do: tranh chấp (chờ tài nguyên dùng chung) và chi phí đồng bộ (các node phải nói chuyện với nhau). Sau một ngưỡng, thêm node còn làm throughput giảm.

Ứng dụng thực tế: khi kết quả load test cho thấy tăng gấp đôi số node chỉ tăng 30% throughput, đó không phải lỗi cấu hình — đó là dấu hiệu có điểm đồng bộ hoặc tài nguyên dùng chung. Phải tìm ra điểm đó, không phải thêm node tiếp.

06Đọc và diễn giải chỉ số

Chỉ sốÝ nghĩa và cạm bẫy
Trung bìnhGần như vô dụng cho trải nghiệm người dùng. Trung bình 200 ms có thể là 90% request 50 ms và 10% request 1,5 s. Không dùng trung bình để đặt SLO.
p95 / p99p99 = 1% request chậm nhất. Với 1 triệu request/ngày, p99 nghĩa là 10.000 người dùng mỗi ngày gặp trải nghiệm đó. Khách hàng lớn thường nằm ở đuôi vì họ có nhiều dữ liệu nhất.
Little's LawConcurrency = Throughput × Latency. Rất hữu dụng để tính số connection cần: 500 req/s × 0,2 s = 100 request đồng thời → pool phải ≥ 100, nếu không đây chính là nút thắt.
SaturationMức sử dụng tài nguyên. Trên 70–80% thì latency bắt đầu tăng phi tuyến. Thiết kế cho 100% sử dụng là thiết kế cho sự cố.
Queue depthChỉ số cảnh báo sớm tốt nhất. Hàng đợi dài lên trước khi latency tăng, và tăng trước khi hệ thống sập.
Error budgetSLO 99,9% = được phép lỗi 43 phút/tháng. Biến "ổn định" từ cảm tính thành ngân sách có thể đàm phán với PM.

07Ba bài toán scale gắn bối cảnh SU02

Ba đề trong Problem Bank lấy trực tiếp từ domain của phòng — đây là chỗ kiến thức chuyển thành thực chiến.

P05 · FMI
Campaign engine
5 triệu thông báo cá nhân hoá/ngày, đỉnh dồn 30 phút, chống gửi trùng, tracking mở/click. Trọng tâm: hệ số đỉnh, idempotency, batching, backpressure.
P06 · Customer 360
Hợp nhất hồ sơ
Gộp dữ liệu từ 4 nguồn, cập nhật gần thời gian thực, truy vấn phân khúc trên hàng triệu bản ghi trong vài giây. Trọng tâm: eventual consistency, CDC, mô hình đọc riêng.
P09 · B2B CRM
Dữ liệu lệch nặng
Vài khách hàng lớn chiếm phần lớn giao dịch — bài toán hot partition kinh điển. Trọng tâm: chọn partition key, tách nhóm khách hàng lớn, tail latency.

08Checklist rà soát thiết kế về performance & scale

Panel dùng checklist này ở mọi buổi Dojo. Người thiết kế dùng để tự rà trước khi trình bày.

NhómCâu hỏi
Con sốTPS trung bình và TPS đỉnh là bao nhiêu? Hệ số đỉnh lấy từ đâu?
Dung lượng sau 1 năm và 3 năm? Có kế hoạch xoá hoặc archive không?
Mục tiêu p95/p99 là bao nhiêu? Ai là người đặt con số đó?
Nút thắtThành phần nào chạm trần trước khi tải tăng 10×?
Điểm đồng bộ hoặc tài nguyên dùng chung nằm ở đâu?
Có thành phần nào không thể scale ngang không? (thường là DB ghi và scheduler)
Dữ liệuPartition key là gì? 10 giá trị phổ biến nhất chiếm bao nhiêu phần trăm?
Truy vấn nào không dùng được index? Truy vấn nào phải quét toàn bảng?
Nếu đọc từ replica, chấp nhận dữ liệu cũ bao lâu?
Hỏng hócMọi lời gọi ra ngoài đã có timeout chưa? Bao nhiêu?
Khi downstream chết 2 giờ thì điều gì xảy ra? Dữ liệu mất hay dồn lại?
Retry có jitter không? Có DLQ không? Ai xử lý DLQ?
Khi quá tải, hệ thống từ chối request nào trước?
Chi phíChi phí hạ tầng ước tính mỗi tháng ở mức tải thiết kế?
Chi phí trên mỗi giao dịch thay đổi thế nào khi tải tăng 10×?

Câu hỏi tổng quát về các cơ chế thường dùng

Đây là câu hỏi định hướng để review, không phải checklist cấu hình của một nền tảng cụ thể.

Cơ chếCâu hỏi cần hỏi trước khi chọn
CacheDữ liệu nào phù hợp để cache? TTL và invalidation dựa trên yêu cầu nghiệp vụ nào? Dữ liệu cũ tối đa bao lâu là chấp nhận được? Nếu cache mất hoàn toàn, hệ thống còn hoạt động không? Có nguy cơ cache stampede hoặc hot key không?
QueueQueue dùng để hấp thụ burst hay tách xử lý bất đồng bộ? Message có thể duplicate hoặc đến sai thứ tự không? Consumer có idempotent không? Có DLQ và người xử lý không? Khi downstream chậm, queue sẽ đầy trong bao lâu?
Connection poolMỗi request giữ connection bao lâu? Tổng pool của mọi instance có vượt giới hạn database không? Khi pool cạn, hệ thống fail fast hay chờ vô hạn? Có transaction giữ connection trong lúc gọi API ngoài không?
RetryLỗi nào được retry và lỗi nào không? Có giới hạn số lần, timeout tổng, backoff và jitter không? Request có idempotent không? Retry ở nhiều tầng có đang khuếch đại tải lên downstream không?
Nguyên tắc chung: mỗi cơ chế giải quyết một loại áp lực nhưng tạo thêm một loại phức tạp. Khi đề xuất cache, queue, pool hoặc retry, phải nêu rõ vấn đề được giải quyết, rủi ro mới tạo ra và cách quan sát khi cơ chế hoạt động sai.

09Anti-patterns về scale

SC-1
Kiến trúc cho tải tưởng tượng
Thiết kế cho 1 triệu user khi hệ thống có 5.000. Trả giá bằng độ phức tạp ngay hôm nay để đổi lấy lợi ích có thể không bao giờ đến.
SC-2
Thiết kế theo tải trung bình
Bỏ qua hệ số đỉnh. Hệ thống chạy ổn 23 giờ và sập đúng giờ khuyến mãi — thời điểm duy nhất nó quan trọng.
SC-3
Microservices chữa bách bệnh
Tách service để giải bài toán performance mà chưa profile. Kết quả: cùng nút thắt cũ, cộng thêm độ trễ mạng và độ phức tạp vận hành.
SC-4
Cache như băng dán
Thêm cache để che một query chậm thay vì sửa query. Khi cache chết, hệ thống sập ngay — vì trạng thái thật chưa bao giờ được sửa.
SC-5
Retry không kiểm soát
Retry không jitter, không giới hạn, ở nhiều tầng lồng nhau. Biến một sự cố nhỏ thành tự tấn công DDoS chính mình.
SC-6
Đo bằng trung bình
Báo cáo latency trung bình cho lãnh đạo. Con số đẹp, khách hàng vẫn than chậm, và không ai hiểu vì sao.