Mở đầu — vì sao bài này quan trọng
Có một câu nói kinh điển trong ngành kỹ thuật hiệu năng: "You can't improve what you don't measure" — bạn không thể cải thiện thứ mình không đo được. Nhưng có một sự thật còn nguy hiểm hơn mà ít ai nói ra: đo sai còn tệ hơn không đo. Khi bạn không đo, bạn biết mình đang mù. Khi bạn đo sai, bạn tưởng mình đang thấy — và bạn tự tin đưa ra những quyết định sai lầm dựa trên con số đẹp đẽ nhưng vô nghĩa.
Ở các bài trước, bạn đã hiểu Performance Testing là gì và có những loại test nào (Load, Stress, Spike, Soak). Nhưng dù bạn chạy loại test nào đi nữa, kết quả cuối cùng vẫn quy về những con số. Và bài học hôm nay là về ba con số cốt lõi mà bất kỳ performance engineer nào cũng phải hiểu tường tận: Latency (độ trễ), Throughput (thông lượng), và Error rate (tỷ lệ lỗi).
Tôi từng chứng kiến một team ở một công ty fintech tại TP.HCM báo cáo với sếp rằng "hệ thống chịu được 5.000 người dùng, thời gian phản hồi trung bình chỉ 800ms". Con số nghe rất ổn. Nhưng khi lên production trong đợt khuyến mãi, hệ thống sập. Vì sao? Vì họ đo trung bình (average) thay vì đo phân vị (percentile), và họ bỏ qua hoàn toàn error rate khi tính latency. Bài học này sẽ giúp bạn tránh đúng cái bẫy đó. Đo đúng metric không phải là kỹ năng nâng cao — nó là nền tảng để mọi thứ khác có ý nghĩa.
Khái niệm cốt lõi
Latency — Response Time (Độ trễ / Thời gian phản hồi)
Latency là thời gian tính từ lúc client gửi request đi cho đến khi nhận được đầy đủ response. Đơn vị thường là milliseconds (ms). Đây là metric mà người dùng cảm nhận trực tiếp nhất — khi bạn bấm nút "Đặt hàng" và màn hình quay tròn, đó chính là latency đang diễn ra.
Nhưng đây là điểm mấu chốt mà 80% người mới mắc phải: đừng bao giờ chỉ nhìn vào giá trị trung bình (average/mean). Average nói dối. Hãy tưởng tượng 10 request: 9 request trả về trong 100ms, và 1 request trả về trong 5.000ms (5 giây). Average = (9×100 + 5000) / 10 = 590ms. Nghe có vẻ chấp nhận được. Nhưng thực tế có một người dùng phải chờ 5 giây — và trên quy mô hàng triệu request mỗi ngày, "1 trong 10" nghĩa là hàng trăm nghìn người dùng đang có trải nghiệm tệ hại.
Đó là lý do chúng ta dùng percentile (phân vị):
- p50 (median / trung vị): 50% request nhanh hơn giá trị này. Đây là trải nghiệm của người dùng "điển hình".
- p90: 90% request nhanh hơn giá trị này. Chỉ 10% chậm hơn.
- p95: 95% request nhanh hơn. Đây là ngưỡng phổ biến nhất để đặt SLA.
- p99: 99% request nhanh hơn. 1% còn lại — nhóm "long tail" — thường là nhóm khách hàng quan trọng nhất (giỏ hàng lớn, session dài, dữ liệu nhiều).
Một lưu ý quan trọng nữa: latency có nhiều "lớp". Có Time To First Byte (TTFB) — thời gian đến byte đầu tiên, phản ánh tốc độ xử lý phía server. Có Connect time — thời gian bắt tay TCP/TLS. Và có tổng thời gian tải toàn bộ response. Khi debug, bạn cần biết chậm ở lớp nào.
Throughput — Thông lượng
Throughput là số lượng công việc hệ thống xử lý được trong một đơn vị thời gian. Thường được đo bằng:
- RPS (Requests Per Second) hoặc QPS (Queries Per Second): số request/giây.
- TPS (Transactions Per Second): số giao dịch/giây (một giao dịch có thể gồm nhiều request).
Có một hiểu lầm rất phổ biến cần đập tan: số lượng virtual user (VU) không phải là throughput. Bạn có thể cấu hình 1.000 VU trong k6 hay JMeter, nhưng throughput thực tế phụ thuộc vào latency. Công thức gần đúng (theo Định luật Little):
> Throughput ≈ Số VU đồng thời / (Latency trung bình + Think time)
Ví dụ: 1.000 VU, mỗi request mất 200ms và không có think time → throughput lý thuyết ≈ 1000 / 0.2 = 5.000 RPS. Nhưng nếu latency tăng lên 1 giây do server quá tải, throughput tụt xuống còn 1.000 RPS — dù bạn vẫn giữ nguyên 1.000 VU. Đây chính là lý do throughput và latency luôn phải nhìn CÙNG NHAU, không bao giờ tách rời.
Một điểm nữa: throughput có "trần" (saturation point). Khi bạn tăng tải, ban đầu throughput tăng tuyến tính. Đến một điểm, throughput chững lại (server đã bão hòa CPU/memory/connection pool), và nếu tiếp tục ép tải, throughput thậm chí GIẢM trong khi latency tăng vọt — hệ thống bắt đầu "nghẹt thở". Tìm ra điểm bão hòa này chính là mục tiêu của nhiều bài test.
Error rate — Tỷ lệ lỗi
Error rate là phần trăm request thất bại trên tổng số request. Nghe đơn giản, nhưng lại là metric bị bỏ quên nhiều nhất — và là metric có sức hủy diệt lớn nhất khi bị bỏ quên.
Vấn đề chí mạng: một request lỗi thường rất nhanh. Server trả về HTTP 500 hoặc 503 chỉ trong 20ms, trong khi request thành công mất 800ms. Nếu bạn tính latency trung bình trên CẢ request lỗi lẫn thành công, các request lỗi nhanh sẽ KÉO latency trung bình xuống, làm bạn tưởng hệ thống đang "nhanh hơn". Thực tế thì ngược lại: hệ thống đang sập, và con số đẹp đó là dấu hiệu của thảm họa, không phải thành công.
Nguyên tắc vàng: chỉ đo latency trên các request THÀNH CÔNG. Đo error rate riêng biệt.
Error rate cần được phân loại chứ không gộp chung:
- HTTP errors: 4xx (lỗi phía client, ví dụ 429 Too Many Requests — bị rate limit) và 5xx (lỗi phía server — 500, 502, 503, 504).
- Network/timeout errors: connection refused, connection timeout, read timeout. Những lỗi này ở tầng TCP/HTTP client, đôi khi không được đếm nếu bạn không cấu hình đúng.
- Assertion failures (lỗi logic): HTTP trả về 200 OK, nhưng nội dung sai — ví dụ trả về trang "Hết hàng" thay vì trang sản phẩm, hoặc body rỗng. Về mặt HTTP là "thành công" nhưng về mặt nghiệp vụ là thất bại. Đây là loại lỗi âm thầm và nguy hiểm nhất.
Ba metric là một bộ ba không thể tách rời
Hãy khắc cốt ghi tâm: latency, throughput và error rate phải luôn được đọc CÙNG NHAU. Một báo cáo performance chỉ có ý nghĩa khi ghi rõ: "Ở throughput X RPS, latency p95 = Y ms, error rate = Z%". Bỏ đi bất kỳ chiều nào, con số còn lại trở nên vô nghĩa hoặc gây hiểu lầm.
Tình huống thực tế
Ví dụ 1: Cái bẫy "average" tại sàn TMĐT ShopFast
ShopFast (tên giả định), một sàn thương mại điện tử tầm trung ở Hà Nội, chuẩn bị cho đợt sale 11.11. Team QA chạy load test và báo cáo đầy tự tin: "Ở 3.000 VU, response time trung bình 650ms, hệ thống sẵn sàng."
Đêm 11.11, khoảng 22h, hệ thống bắt đầu treo cứng ở bước thanh toán. Điều tra sau đó phát hiện: latency trung bình đúng là 650ms, nhưng p95 lên tới 4.200ms và p99 là 9.000ms. Nhóm khách hàng có giỏ hàng lớn (nhiều sản phẩm, tính phí ship phức tạp) rơi hết vào cái đuôi p99 — chính là nhóm khách chi tiền nhiều nhất. Họ chờ 9 giây, timeout, rồi bỏ giỏ hàng.
Bài học rút ra: Trung bình che giấu nỗi đau ở phần đuôi. Nếu team đã đặt SLA theo p95/p99 ngay từ đầu, họ đã phát hiện vấn đề trong phòng test chứ không phải giữa đêm sale. Từ đó, quy tắc của team đổi thành: mọi báo cáo latency bắt buộc có p50/p95/p99, average chỉ để tham khảo.
Ví dụ 2: Error rate "ẩn" làm đẹp con số tại một ví điện tử
Một ví điện tử Đông Nam Á chạy stress test cổng nạp tiền. Kết quả ban đầu: "Ở 8.000 RPS, latency trung bình chỉ 120ms — quá tốt!". Sếp gần như đã duyệt lên production.
May mắn là một kỹ sư senior để ý con số quá đẹp một cách bất thường (120ms cho một luồng gọi ngân hàng là không thực tế). Anh mở dashboard chi tiết và phát hiện: error rate là 34%. Một phần ba số request bị trả về HTTP 503 chỉ trong 15ms vì connection pool tới ngân hàng đã cạn. Chính những response lỗi siêu nhanh này đã kéo latency trung bình xuống 120ms. Khi tính lại latency CHỈ trên request thành công, con số thật là p95 = 2.800ms.
Bài học rút ra: Luôn tách error rate ra và chỉ đo latency trên request thành công. Một con số latency quá đẹp thường là dấu hiệu của lỗi bị che giấu, không phải hiệu năng xuất sắc. Từ đó team đặt thêm một "cổng gác" (threshold): nếu error rate > 1%, kết quả test tự động bị coi là FAIL bất kể latency đẹp đến đâu.
Ví dụ 3: Throughput trần và điểm bão hòa của một API nội bộ
Một công ty logistics tại TP.HCM cần biết API tra cứu đơn hàng chịu được bao nhiêu tải. Họ chạy load test tăng dần: 500 → 1.000 → 2.000 → 4.000 VU.
Dữ liệu thu được rất "biết nói":
- Ở 500 VU: throughput 480 RPS, p95 = 210ms, error 0%.
- Ở 1.000 VU: throughput 950 RPS, p95 = 240ms, error 0%.
- Ở 2.000 VU: throughput 1.400 RPS, p95 = 1.100ms, error 0.3%.
- Ở 4.000 VU: throughput chỉ còn 1.350 RPS, p95 = 6.500ms, error 12%.
Bài học rút ra: Throughput không tăng vô hạn theo số VU. Việc đọc đồng thời cả ba metric qua nhiều mức tải mới cho bạn thấy "trần" thật sự của hệ thống — con số vàng để làm capacity planning (sẽ học sâu ở bài sau).
Hướng dẫn từng bước
Đây là quy trình đọc và diễn giải metric đúng chuẩn cho mỗi lần chạy test:
Bước 1 — Xác định request thành công vs thất bại TRƯỚC khi đọc latency. Định nghĩa rõ "thành công" là gì: HTTP 2xx/3xx VÀ nội dung đúng nghiệp vụ (dùng assertion/check). Bất cứ thứ gì khác là lỗi.
Bước 2 — Tính error rate riêng. Error rate = (số request lỗi / tổng request) × 100%. Phân loại theo nhóm: 4xx, 5xx, timeout, assertion fail. Nếu error rate vượt ngưỡng bạn đặt (thường 1%), coi như test đã fail — không cần bàn latency nữa, hãy đi tìm nguyên nhân lỗi trước.
Bước 3 — Đọc latency CHỈ trên request thành công, theo percentile. Ghi lại tối thiểu p50, p95, p99. Đừng chỉ ghi average. Nếu công cụ cho phép, xem thêm min/max để hiểu độ phân tán.
Bước 4 — Đọc throughput ở trạng thái ổn định (steady state). Bỏ qua giai đoạn ramp-up (tăng tải) và ramp-down (giảm tải); chỉ lấy throughput ở đoạn tải ổn định giữa test. Ghi rõ throughput đạt được TƯƠNG ỨNG với mức tải nào.
Bước 5 — Ghép ba metric thành một câu kết luận. Ví dụ: "Ở 1.500 RPS, p95 latency = 320ms, error rate = 0.2% — đạt SLA." Một câu như vậy mới là một kết luận performance hoàn chỉnh.
Bước 6 — So sánh với ngưỡng/SLA đã định trước. Metric chỉ có ý nghĩa khi có mốc so sánh. Nếu chưa có SLA, hãy dùng lần chạy này làm baseline cho các lần sau.
Về mặt công cụ (sẽ đi sâu ở các bài JMeter và k6): trong JMeter, các listener như Aggregate Report và Summary Report cho bạn cột 90% Line, 95% Line, 99% Line, Throughput và Error %. Trong k6, các metric mặc định như http_req_duration (có sẵn p90, p95), http_reqs (throughput) và http_req_failed (error rate) chính là ba metric của bài học này. Bạn sẽ dùng chúng liên tục.
Lỗi thường gặp & mẹo
Lỗi 1 — Chỉ báo cáo average. Đây là lỗi phổ biến nhất. Luôn dùng percentile. Nếu buộc phải chọn một con số duy nhất để đại diện, hãy chọn p95, đừng chọn average.
Lỗi 2 — Tính latency gộp cả request lỗi. Như đã phân tích, request lỗi thường nhanh và sẽ bóp méo latency. Luôn lọc lỗi ra trước.
Lỗi 3 — Bỏ quên error rate. Một báo cáo latency đẹp mà không kèm error rate là một báo cáo vô giá trị. Luôn đặt error rate cạnh latency.
Lỗi 4 — Nhầm số VU với throughput. VU là "đầu vào" bạn cấu hình; throughput là "đầu ra" hệ thống thực sự đạt được. Chúng khác nhau và mối liên hệ phụ thuộc vào latency.
Lỗi 5 — Không tính đến giai đoạn warm-up. Những giây đầu tiên khi cache còn lạnh, JIT chưa tối ưu, connection pool chưa đầy... thường có latency cao bất thường. Loại bỏ giai đoạn warm-up khỏi con số cuối để tránh làm xấu kết quả một cách oan uổng.
Lỗi 6 — Bỏ qua độ trễ do "coordinated omission". Đây là một cạm bẫy tinh vi: nhiều công cụ chỉ gửi request tiếp theo SAU KHI request trước trả về. Khi server chậm, công cụ vô tình gửi ít request hơn trong lúc chậm, khiến các mẫu latency cao bị đếm thiếu — báo cáo đẹp hơn thực tế. Hãy dùng công cụ/chế độ có "open model" (như một số executor của k6) khi cần đo chính xác dưới tải cao.
Mẹo 1 — Luôn đặt threshold/SLA trước khi chạy. Ví dụ: p95 < 500ms và error < 1%. Có ngưỡng trước giúp kết quả tự phán "pass/fail" thay vì bạn nhìn con số mà tự huyễn hoặc.
Mẹo 2 — Vẽ biểu đồ theo thời gian, đừng chỉ nhìn con số tổng. Một biểu đồ latency/throughput/error theo trục thời gian cho bạn thấy xu hướng: latency có tăng dần không (dấu hiệu memory leak), error có bùng lên ở phút thứ mấy không.
Mẹo 3 — Ghi lại bối cảnh. Cùng một con số 800ms có thể tốt cho một báo cáo phức tạp và tệ cho một API health-check. Metric không có bối cảnh là metric chết.
Bài tập thực hành
- Tính percentile bằng tay. Cho tập latency (ms) của 10 request: 90, 95, 100, 105, 110, 120, 130, 150, 800, 2000. Hãy tính: average, p50, p90. So sánh average với p50 và giải thích tại sao chúng chênh nhau, cái nào phản ánh trải nghiệm điển hình hơn.
- Phát hiện lỗi bị che giấu. Một báo cáo ghi: "10.000 request, latency trung bình 90ms, 2.500 request trả về HTTP 500." Hãy tính error rate. Giải thích vì sao con số 90ms có thể đang gây hiểu lầm, và bạn sẽ tính lại latency thế nào cho đúng.
- Ước lượng throughput. Bạn cấu hình 600 VU, mỗi VU thực hiện request tuần tự, latency trung bình mỗi request là 300ms, think time = 0. Dùng Định luật Little để ước lượng throughput lý thuyết (RPS). Sau đó, nếu latency tăng lên 1.500ms do quá tải, throughput mới là bao nhiêu?
- Tìm điểm bão hòa. Cho bảng: (500 VU → 490 RPS, p95 200ms, err 0%), (1.000 VU → 900 RPS, p95 350ms, err 0%), (2.000 VU → 1.100 RPS, p95 1.800ms, err 5%), (3.000 VU → 1.050 RPS, p95 4.000ms, err 18%). Hãy xác định điểm bão hòa và viết một câu kết luận performance hoàn chỉnh gồm cả ba metric.
- Viết threshold. Cho một API thanh toán, hãy tự đề xuất một bộ SLA gồm ngưỡng cho p95, p99 và error rate, kèm lý do vì sao API thanh toán cần ngưỡng khắt khe hơn một API tra cứu thông tin.
Tóm tắt
- Latency, Throughput, Error rate là bộ ba metric cốt lõi của mọi performance test, và phải luôn được đọc CÙNG NHAU.
- Latency đo trải nghiệm người dùng — nhưng phải dùng percentile (p50, p95, p99), tuyệt đối không tin vào average. Cái đuôi (p99) định đoạt chất lượng dịch vụ.
- Throughput (RPS/TPS) đo năng lực hệ thống. Nó KHÁC với số VU, phụ thuộc vào latency, và có điểm bão hòa — vượt qua thì throughput tụt còn latency vọt lên.
- Error rate là metric bị bỏ quên nhiều nhất nhưng nguy hiểm nhất. Request lỗi thường nhanh và sẽ bóp méo latency, nên chỉ đo latency trên request thành công và tách error rate ra riêng, phân loại rõ (4xx, 5xx, timeout, assertion fail).
- Một kết luận performance đúng chuẩn luôn có dạng: "Ở throughput X, p95 latency = Y, error rate = Z%, so với SLA đã định."
- Đo sai metric dẫn đến quyết định sai. Ba câu chuyện thực tế trong bài đều bắt nguồn từ cùng một gốc rễ: tin vào average và bỏ qua error rate. Đừng lặp lại sai lầm đó.