Mở đầu — vì sao bài này quan trọng
Có một sự thật phũ phàng trong nghề Performance Testing: chạy được một bài test không khó, nhưng đọc đúng kết quả của nó mới là ranh giới phân biệt giữa một người mới và một kỹ sư thật sự. Rất nhiều bạn sau khi bấm nút chạy JMeter hoặc k6, nhìn thấy màn hình đầy con số rồi... bối rối. Họ báo cáo với sếp rằng "hệ thống chịu được 1000 user" chỉ vì test không văng lỗi, trong khi thực tế thời gian phản hồi đã leo lên 8 giây — một con số mà không khách hàng nào chịu nổi.
Ở những bài trước bạn đã học cách viết test plan, cách cấu hình Thread Group, Options & Stages, cách đặt Checks & Thresholds. Bài này là mắt xích còn thiếu: sau khi test chạy xong, làm sao biến đống số liệu thô thành kết luận có ý nghĩa. Chúng ta sẽ tập trung vào việc đọc và diễn giải output của cả hai công cụ — JMeter HTML Dashboard và k6 summary/end-of-test report. Lưu ý: bài này chỉ dạy cách đọc kết quả. Việc truy tìm nguyên nhân điểm nghẽn (bottleneck) là chủ đề riêng của Bài 25, còn viết findings và báo cáo chính thức thuộc Bài 38 — ở đây ta xây nền tảng đọc số cho cả hai.
Khái niệm cốt lõi
Đừng bao giờ nhìn giá trị trung bình (average) một mình
Đây là bài học đắt giá nhất mà mọi kỹ sư performance phải khắc cốt ghi tâm. Giá trị average (trung bình) che giấu sự thật. Giả sử bạn có 100 request: 99 request trả về trong 100ms, 1 request mất 10 giây. Average sẽ là khoảng 199ms — nghe rất đẹp. Nhưng có 1% người dùng đang phải chờ 10 giây, và với hệ thống hàng triệu request mỗi ngày, 1% đó là hàng chục nghìn người bực bội.
Vì thế, dân trong nghề tin vào percentile. Percentile thứ 90 (p90) nghĩa là 90% request nhanh hơn giá trị đó. p95, p99 tương tự. Con số vàng thường là p95 — nó phản ánh trải nghiệm của đại đa số người dùng mà vẫn không bị vài outlier cực đoan làm nhiễu. Khi ai đó hỏi "hệ thống nhanh không?", câu trả lời chuẩn phải là "p95 khoảng 320ms" chứ không phải "trung bình 180ms".
Bộ số liệu bắt buộc phải đọc
Dù dùng JMeter hay k6, bạn luôn cần bốn nhóm số sau:
- Response time / Latency: thời gian phản hồi. Đọc theo min, median (p50), p90, p95, p99, max. Max cho thấy trường hợp tệ nhất, p95 cho thấy trải nghiệm thực tế.
- Throughput: số request xử lý được mỗi giây (JMeter gọi là Transactions/sec, k6 là http_reqs per second hoặc iterations/s). Đây là năng lực thật của hệ thống.
- Error rate: tỷ lệ lỗi. Trong JMeter là cột Error %, trong k6 là http_req_failed. Một con số nhỏ như 0.5% cũng có thể là dấu hiệu hệ thống bắt đầu quá tải.
- Số lượng samples / requests: tổng số phép đo. Quan trọng để biết kết quả có đủ độ tin cậy thống kê hay không — 20 mẫu thì đừng vội kết luận gì cả.
JMeter HTML Dashboard
JMeter tạo báo cáo HTML từ file kết quả .jtl. Quy trình chuẩn: chạy test ở non-GUI mode để xuất ra result.jtl, rồi sinh dashboard:
jmeter -g result.jtl -o report
Sau đó mở report/index.html trong trình duyệt. Bạn sẽ thấy:
- APDEX (Application Performance Index): một chỉ số 0–1 gói gọn mức độ hài lòng, dựa trên ngưỡng T (satisfied) và F (tolerated) bạn cấu hình. 1.0 là hoàn hảo, dưới 0.5 là báo động.
- Statistics table — bảng quan trọng nhất. Mỗi dòng là một request label (ví dụ "Login", "Search", "Checkout") với các cột:
#Samples,Average,Median,90% Line,95% Line,99% Line,Min,Max,Error %,Throughput,Received KB/sec. Đọc bảng này là đọc "nhịp tim" của hệ thống. - Response Times Over Time và Active Threads Over Time: hai biểu đồ đặt cạnh nhau sẽ cho bạn thấy khi số user tăng thì response time tăng ra sao — dấu hiệu kinh điển của việc chạm giới hạn.
- Response Time Percentiles: đường cong percentile. Nếu đường này dốc đứng ở cuối (p99, p99.9 vọt lên), hệ thống có "long tail" — vài request cực chậm.
k6 end-of-test summary
k6 in ra summary ngay trên terminal khi test kết thúc. Các dòng cần đọc:
http_req_duration: có avg, min, med, max, p(90), p(95). Đây là latency chính.http_req_failed: tỷ lệ request thất bại, ví dụ0.12% ✓ 3 ✗ 2497.http_reqs: tổng số request và rate mỗi giây (throughput).iterationsvàiteration_duration: một iteration là một vòng lặp của kịch bản (user journey). Rất hữu ích khi đo cả một luồng, không chỉ từng request lẻ.vusvàvus_max: số virtual user đang chạy và tối đa.- Nếu bạn đã đặt Thresholds, k6 hiển thị dấu ✓ (pass) hoặc ✗ (fail) ngay cạnh — và exit code khác 0 khi fail, cực kỳ tiện cho CI/CD.
k6 run --out json=out.json script.js) để phân tích sâu hơn, hoặc đẩy sang InfluxDB/Grafana để xem trực quan — nhưng phần dashboard sống đó là chủ đề của Bài 22 và 55.Tình huống thực tế
Tình huống 1: Cái bẫy "average đẹp" tại một sàn TMĐT Việt Nam
Một team QA tại một sàn thương mại điện tử lớn ở TP.HCM (giả định gọi là ShopViet) chạy load test cho trang danh mục sản phẩm với 500 concurrent user. Kết quả JMeter Statistics table:
| Label | #Samples | Average | Median | 95% Line | Max | Error % |
|---|---|---|---|---|---|---|
| Category Page | 45,000 | 210ms | 95ms | 1,850ms | 9,400ms | 0.00% |
Bài học rút ra: Con số "0% lỗi" và "average thấp" đánh lừa người đọc thiếu kinh nghiệm. Chính khoảng cách giữa median và p95/max mới hé lộ vấn đề. Luôn đọc percentile trước, average sau.
Tình huống 2: Error rate 0.3% nói lên điều gì tại một ví điện tử
Một team ở một công ty fintech tại Hà Nội chạy k6 test API xác thực OTP với ramp lên 2,000 VUs. Summary k6:
http_req_duration...: avg=340ms p(95)=680ms max=14.2s
http_req_failed.....: 0.34% ✓ 68 ✗ 19,932
http_reqs...........: 20,000 666.6/s
Response time trông chấp nhận được, nhưng max=14.2s và 68 request lỗi khiến team để ý. Họ xuất JSON và lọc ra 68 request thất bại — tất cả đều là HTTP 500 và đều rơi vào 30 giây cuối của giai đoạn peak. Đây không phải lỗi ngẫu nhiên: đó là dấu hiệu hệ thống bắt đầu "gãy" khi chạm ngưỡng tải. 0.34% hôm nay ở phòng lab, nhưng vào giờ cao điểm thật với lưu lượng gấp đôi, con số đó có thể thành 5–10%.
Bài học rút ra: Đừng bao giờ bỏ qua error rate dù nhỏ. Điều quan trọng không chỉ là bao nhiêu lỗi mà là khi nào và loại lỗi gì. Lỗi tập trung vào cuối giai đoạn peak là tín hiệu quá tải, khác hẳn lỗi rải rác đều (thường là lỗi test data hoặc network).
Tình huống 3: Throughput không tăng dù thêm user — "trần" của hệ thống
Một startup logistics ở Singapore test API tra cứu đơn hàng. Họ tăng dần VUs từ 100 lên 1,500 và ghi lại throughput qua biểu đồ Grafana kết nối với k6:
- 100 VUs → 480 req/s, p95 = 180ms
- 500 VUs → 2,050 req/s, p95 = 240ms
- 1,000 VUs → 2,400 req/s, p95 = 950ms
- 1,500 VUs → 2,420 req/s, p95 = 3,100ms
Hướng dẫn từng bước
Bước 1 — Sinh báo cáo có thể đọc được. Với JMeter, luôn chạy non-GUI rồi generate dashboard: jmeter -n -t plan.jmx -l result.jtl rồi jmeter -g result.jtl -o report. Với k6, chạy k6 run script.js để lấy summary; thêm --out json=out.json nếu cần phân tích sâu.
Bước 2 — Kiểm tra tính hợp lệ của test trước tiên. Trước khi tin bất kỳ con số nào, xác nhận: test có chạy đủ thời gian không? Error % có phải do lỗi test (sai endpoint, hết test data) không? Số samples có đủ lớn không? Một test lỗi cấu hình sẽ cho ra số liệu vô nghĩa.
Bước 3 — Đọc error rate đầu tiên. Nếu Error % / http_req_failed cao bất thường, dừng lại phân tích lỗi trước. Response time của một hệ thống đang trả lỗi 500 hàng loạt là vô nghĩa (trả lỗi thường rất nhanh, làm đẹp số giả tạo).
Bước 4 — Đọc percentile, không đọc average. Ghi lại p95 (và p99 nếu SLA khắt khe) cho từng request quan trọng. So sánh median với p95: khoảng cách lớn = phân phối lệch = có vấn đề tiềm ẩn.
Bước 5 — Đọc throughput cùng với response time. Nhìn hai con số này cùng nhau theo mức tải. Throughput là năng lực; response time là trải nghiệm. Điểm mà throughput ngừng tăng còn response time bắt đầu vọt lên chính là ngưỡng công suất.
Bước 6 — Đối chiếu với SLA/threshold. So từng con số với mục tiêu đã định (ví dụ p95 < 500ms, error < 1%). Với k6, dấu ✓/✗ và exit code làm sẵn việc này. Kết luận cuối cùng phải là "đạt" hoặc "không đạt" so với tiêu chí rõ ràng.
Lỗi thường gặp & mẹo
- Chỉ nhìn average và báo cáo nó: sai lầm số một. Luôn dẫn p95, kèm max để cảnh báo long tail.
- Bỏ qua giai đoạn ramp-up khi tính số: trong lúc số user đang tăng dần, response time chưa ổn định. Nếu tính trung bình cả giai đoạn ramp-up lẫn steady-state, số bị nhiễu. Mẹo: JMeter cho phép lọc theo thời gian; k6 có thể tách metric theo từng stage bằng tag/group (Bài 20).
- Tin vào test có quá ít samples: 30 request không đủ để p99 có ý nghĩa thống kê. Cần hàng nghìn mẫu cho các percentile cao.
- Quên rằng lỗi làm nhanh response time giả tạo: nếu 40% request trả 500 trong 5ms, average tụt xuống rất đẹp nhưng hệ thống đang chết. Luôn đọc error rate trước response time.
- Mở HTML Dashboard trực tiếp từ file:// và thấy biểu đồ trống: một số trình duyệt chặn tải asset. Mẹo: phục vụ thư mục report qua HTTP đơn giản, ví dụ
python3 -m http.servertrong thư mụcreport. - So sánh kết quả giữa các lần chạy trên môi trường khác nhau: kết quả chỉ có ý nghĩa khi so trên cùng cấu hình, cùng test data, cùng network. Ghi rõ ngữ cảnh mỗi lần đo.
- Mẹo vàng: Luôn ghi lại bộ ba "p95 – throughput – error rate ở mức tải X" như một tuple. Đó là ngôn ngữ chung mà mọi kỹ sư performance đều hiểu ngay.
Bài tập thực hành
- Đọc bảng Statistics: Cho một request có Median = 120ms, 95% Line = 2,400ms, Max = 11,000ms, Error % = 0%. Hãy viết ba câu kết luận về sức khỏe của request này và giải thích tại sao "average thấp" ở đây có thể gây hiểu lầm.
- So sánh hai công cụ: Chạy cùng một kịch bản đơn giản (GET một endpoint công khai như
https://test-api.k6.io) bằng cả JMeter và k6 với 50 user trong 2 phút. Đối chiếu p95 và throughput giữa JMeter Statistics table và k6 summary. Chúng có khớp nhau không? Ghi lại chênh lệch và suy nghĩ về nguyên nhân.
- Tìm ngưỡng bão hòa: Chạy k6 với các stage tăng dần 100 → 300 → 600 → 1000 VUs. Ghi lại throughput (http_reqs/s) và p95 ở mỗi mức. Vẽ tay hai đường trên giấy và xác định điểm throughput ngừng tăng — đó là công suất tối đa ước lượng của hệ thống.
- Điều tra error timing: Xuất JSON từ một lần chạy k6 có lỗi (
--out json=out.json), lọc các dònghttp_req_failedbằng giá trị 1, và xác định các lỗi tập trung ở đầu, giữa hay cuối test. Rút ra kết luận về nguyên nhân khả dĩ.
Tóm tắt
Đọc kết quả performance test không phải là liếc qua vài con số rồi gật đầu. Cốt lõi nằm ở bốn nguyên tắc: (1) percentile quan trọng hơn average — luôn dẫn p95 và soi max để phát hiện long tail; (2) error rate phải đọc đầu tiên — vì lỗi có thể làm response time đẹp một cách giả tạo, và thời điểm lỗi xảy ra tiết lộ bản chất vấn đề; (3) throughput và response time phải đọc cùng nhau — điểm throughput chạm trần trong khi response time vọt lên chính là công suất thật của hệ thống; (4) mọi con số chỉ có nghĩa khi đặt trong ngữ cảnh — cùng môi trường, đủ samples, so với SLA rõ ràng.
Với JMeter, bảng Statistics trong HTML Dashboard là trung tâm; với k6, end-of-test summary cùng dấu ✓/✗ của threshold là điểm khởi đầu. Nắm vững cách đọc chúng, bạn đã sẵn sàng cho bước tiếp theo: truy tìm nguyên nhân của những con số xấu — chính là nội dung Bài 25 về Bottleneck Analysis.