Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Spike & Scalability testing

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn là kỹ sư hiệu năng cho một sàn thương mại điện tử Việt Nam. Hệ thống của bạn chạy êm ru với 5.000 người dùng đồng thời suốt cả năm. Rồi đến 0h ngày 12/12, chương trình "Flash Sale 90%" mở màn. Trong vòng 30 giây, lượng truy cập nhảy vọt lên 80.000 người. Server phản hồi chậm dần, rồi timeout hàng loạt, rồi... sập. Ba mươi phút vàng của chiến dịch marketing trị giá hàng tỷ đồng bốc hơi vì trang giỏ hàng trả về lỗi 503.

Câu hỏi đặt ra: tại sao suốt cả năm test load bình thường vẫn ổn, mà đúng lúc quan trọng nhất lại đổ vỡ? Vì test load thông thường mô phỏng tải tăng dần và ổn định, còn thực tế đời sống lại đầy những cú tăng sốc (spike) và những đòi hỏi phải mở rộng năng lực (scalability) mà không phải ai cũng chủ động kiểm tra.

Bài này tập trung vào hai kỹ thuật anh em nhưng khác biệt về mục tiêu: Spike Test — kiểm tra khả năng chịu đựng và phục hồi trước cú tăng tải đột ngột; và Scalability Test — đo xem hệ thống mở rộng năng lực đến đâu và có "mở rộng đàng hoàng" không. Nắm vững hai kỹ thuật này, bạn sẽ trả lời được những câu hỏi mà sếp và team vận hành luôn hỏi trước mỗi chiến dịch lớn: "Nếu traffic gấp 10 lần trong 1 phút, hệ thống sống hay chết? Và cần thêm bao nhiêu server để cân được?"

Khái niệm cốt lõi

Spike Test là gì

Spike Test mô phỏng một cú tăng tải đột ngột trong thời gian rất ngắn rồi thường giảm về mức bình thường ngay sau đó. Điểm mấu chốt không nằm ở con số tải đỉnh, mà nằm ở tốc độ thay đổi (rate of change). Load Test tăng từ 0 lên 10.000 user trong 10 phút là chuyện bình thường; Spike Test tăng từ 500 lên 10.000 user trong 30 giây — đó mới là cú sốc.

Spike Test trả lời ba câu hỏi:

  • Hệ thống có sống sót qua cú tăng đột ngột không? (survivability)
  • Nó phản hồi ra sao trong lúc bị sốc? — lỗi tăng bao nhiêu, latency đội lên thế nào.
  • Quan trọng nhất: nó có phục hồi (recovery) về trạng thái bình thường sau khi tải giảm không? — hay là "chết luôn" dù tải đã hạ.
Yếu tố recovery chính là linh hồn của Spike Test. Nhiều hệ thống chịu được cú sốc trong lúc nó diễn ra, nhưng khi tải giảm lại không tự hồi phục: connection pool cạn kiệt không được trả về, thread bị treo, hàng đợi (queue) dồn ứ khiến các request sau đó vẫn tiếp tục lỗi dù chẳng còn ai truy cập. Đó là dấu hiệu của một kiến trúc thiếu cơ chế tự bảo vệ.

Scalability Test là gì

Scalability Test đo năng lực mở rộng của hệ thống. Thay vì chỉ hỏi "chịu được bao nhiêu?", nó hỏi "khi ta tăng tài nguyên (hoặc tải), hệ thống phản ứng thế nào?". Có hai chiều mở rộng:

  • Scale up (vertical / theo chiều dọc): tăng sức mạnh một máy — thêm CPU, RAM. Test xem hiệu năng cải thiện tương xứng không.
  • Scale out (horizontal / theo chiều ngang): thêm nhiều máy/instance. Test xem thêm gấp đôi server thì throughput có tăng gần gấp đôi không.
Ý tưởng trọng tâm của Scalability Test là tính tuyến tính (linearity). Một hệ thống mở rộng lý tưởng: tăng gấp đôi tài nguyên → gấp đôi năng lực. Thực tế hiếm khi hoàn hảo vì luôn có phần "không song song hoá được" — đây chính là nội dung của Định luật Amdahl. Nếu 10% khối lượng công việc buộc phải chạy tuần tự (ví dụ ghi vào một database master duy nhất), thì dù bạn thêm bao nhiêu server, năng lực cũng bị chặn trần bởi cái 10% ấy.

Phân biệt Spike với Stress và Load

Nhiều bạn mới hay lẫn lộn. Xin nhắc ngắn để phân định ranh giới (chi tiết từng loại đã có ở các bài trước):

  • Load Test: tải kỳ vọng, tăng dần, duy trì. Trả lời "chạy tốt ở mức bình thường không?".
  • Stress Test: tăng vượt giới hạn để tìm điểm gãy. Trả lời "gãy ở đâu?".
  • Spike Test: tăng cực nhanh rồi rút. Trả lời "chịu cú sốc và phục hồi được không?".
  • Scalability Test: thay đổi tài nguyên/tải theo bước, đo tính tuyến tính. Trả lời "mở rộng đến đâu, hiệu quả ra sao?".
Điểm khác biệt cốt lõi: Stress quan tâm mức tải đỉnh, còn Spike quan tâm tốc độ đạt đỉnh và khả năng rút lui.

Cấu trúc một kịch bản Spike trong k6

Trong k6, Spike Test được thể hiện rõ nhất qua các stages. Chú ý những chặng duration cực ngắn nhưng target nhảy vọt:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = { stages: [ { duration: '1m', target: 100 }, // baseline ổn định { duration: '30s', target: 3000 }, // CÚ SPIKE: 100 -> 3000 trong 30s { duration: '1m', target: 3000 }, // giữ đỉnh, quan sát hành vi { duration: '30s', target: 100 }, // rút về baseline { duration: '2m', target: 100 }, // GIAI ĐOẠN RECOVERY - quan trọng nhất { duration: '30s', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<800'], http_req_failed: ['rate<0.05'], }, };

export default function () { const res = http.get('https://shop.example.vn/api/products'); check(res, { 'status is 200': (r) => r.status === 200, 'response < 1s': (r) => r.timings.duration < 1000, }); sleep(1); }

Chặng 2m target 100 sau khi rút khỏi đỉnh chính là cửa sổ quan sát recovery. Nếu error rate và latency ở chặng này vẫn cao (dù tải chỉ còn 100 user như baseline ban đầu), nghĩa là hệ thống không phục hồi được — một phát hiện cực kỳ giá trị.

Tình huống thực tế

Tình huống 1 — Cú spike "vé concert" và bài học về hàng đợi

Một nền tảng bán vé sự kiện tại TP.HCM (gọi là TicketVN) mở bán 30.000 vé cho một đêm nhạc lớn lúc đúng 10h sáng. Team dựa vào kinh nghiệm Load Test cũ: hệ thống từng gánh 8.000 user đồng thời rất mượt, nên họ tự tin.

Thực tế lúc 10h00: khoảng 120.000 người bấm F5 cùng lúc chờ mở bán. Trong 20 giây đầu, request tăng gấp 15 lần so với baseline. Kết quả: API check tồn kho vé quá tải, database connection pool (giới hạn 200 kết nối) cạn sạch trong 8 giây, và điều tệ nhất — sau khi họ chặn bớt traffic ở tầng CDN để cứu, hệ thống vẫn không phục hồi suốt 15 phút vì hàng đợi kết nối bị dồn ứ, các request cũ timeout xong vẫn giữ khoá.

Khi mô phỏng lại bằng Spike Test trong k6 (spike lên 20.000 VU trong 20s), team phát hiện đúng vấn đề: recovery window vẫn đỏ lòm error 100% dù đã rút tải. Họ khắc phục bằng ba biện pháp: (1) đưa vào cơ chế virtual waiting room — hàng đợi ảo cho user xếp hàng có trật tự thay vì dội thẳng vào backend; (2) thêm rate limiting ở tầng gateway; (3) cấu hình connection pool timeout ngắncircuit breaker để cắt mạch khi database quá tải, tránh dồn ứ.

Bài học: cú spike phơi bày điểm nghẽn mà Load Test không bao giờ thấy. Và "sống sót qua đỉnh" chưa đủ — phải kiểm tra khả năng phục hồi sau đỉnh.

Tình huống 2 — Scalability Test và định luật Amdahl trong đời thực

Một công ty fintech ở Hà Nội (gọi là PayFast) chạy hệ thống xử lý giao dịch trên Kubernetes. Sếp yêu cầu: "Cứ thêm pod là gánh thêm tải, đơn giản mà." Team quyết định làm Scalability Test đàng hoàng thay vì tin lời hứa.

Họ chạy cùng một kịch bản k6 (giữ tải cố định ở mức đủ bão hoà) qua bốn cấu hình: 2 pod, 4 pod, 8 pod, 16 pod, và ghi lại throughput (request/giây) mỗi lần:

Số podThroughput (req/s)Tỷ lệ so với lý tưởng
21.000100% (mốc)
41.90095%
83.40085%
165.20065%
Con số kể một câu chuyện rõ ràng: từ 8 lên 16 pod, throughput chỉ tăng thêm ~53% thay vì gấp đôi. Đường cong đang phẳng dần — dấu hiệu kinh điển của điểm nghẽn tuần tự. Đào sâu, họ tìm ra thủ phạm: mọi giao dịch đều phải ghi vào một database master duy nhất, và bảng giao dịch dùng một sequence khoá bi quan (pessimistic lock) để sinh mã giao dịch. Đó chính là phần 10-15% "không song song hoá được" trong định luật Amdahl.

Kết luận thực dụng: thêm pod quá con số 12 là lãng phí tiền cloud vì database mới là nút thắt. Team chuyển hướng đầu tư sang sharding database và dùng thuật toán sinh ID phân tán (kiểu Snowflake) thay cho sequence khoá.

Bài học: Scalability Test không chỉ nói "chịu được bao nhiêu", nó chỉ ra điểm mà việc thêm tài nguyên trở nên vô nghĩa — thông tin vàng cho capacity planning và cho ví tiền.

Tình huống 3 — Auto-scaling và cái bẫy "warm-up"

Một startup giao đồ ăn (gọi là FoodNow) tự tin vì đã bật auto-scaling trên AWS: "CPU vượt 70% là tự thêm instance." Nhưng khi chạy Spike Test mô phỏng giờ cao điểm bữa trưa (tăng gấp 6 lần trong 45 giây), họ nhận ra sự thật đau lòng: auto-scaling phản ứng quá chậm so với cú spike.

Quy trình thực tế: metric CPU cần ~1 phút để đủ dữ liệu kích hoạt, instance mới cần ~2 phút để khởi động và warm-up (JVM khởi động, cache còn lạnh, kết nối database chưa sẵn sàng). Tổng cộng ~3 phút. Trong khi đó, cú spike chỉ kéo dài 45 giây rồi đạt đỉnh — nghĩa là khi instance mới sẵn sàng thì "trận đánh đã kết thúc" và khách đã bỏ đi.

Spike Test giúp họ định lượng được độ trễ scaling này. Giải pháp: (1) pre-warming — chủ động scale up trước giờ cao điểm theo lịch (scheduled scaling) thay vì chỉ dựa reactive; (2) giữ một lớp buffer capacity thường trực; (3) tối ưu thời gian khởi động instance bằng image đã cấu hình sẵn.

Bài học: có auto-scaling không có nghĩa là an toàn trước spike. Tốc độ scaling phải nhanh hơn tốc độ spike, nếu không nó chỉ giúp bạn... phục hồi sau khi đã thiệt hại.

Hướng dẫn từng bước

Dưới đây là quy trình thực hành một chu trình Spike + Scalability Test hoàn chỉnh.

Bước 1 — Xác định baseline và tải đỉnh mục tiêu. Tìm ra tải bình thường (ví dụ 500 VU) và ước lượng tải đỉnh thực tế có thể xảy ra. Với thương mại điện tử VN dịp sale, hãy nhân traffic thường ngày với 10-20 lần. Đừng đoán mò — dựa vào dữ liệu Google Analytics của chiến dịch trước hoặc số liệu vận hành.

Bước 2 — Thiết kế profile spike. Quyết định ba tham số: tải baseline, tải đỉnh, và thời gian đạt đỉnh (yếu tố quyết định "độ sốc"). Luôn thêm một recovery stage đủ dài (2-3 phút) sau khi rút tải để quan sát phục hồi.

Bước 3 — Chuẩn bị test data và môi trường. Spike Test cần dữ liệu thật (tài khoản user, sản phẩm, mã giảm giá) đủ lớn để không bị cache che mất vấn đề. Chạy trên môi trường giống production về tỷ lệ — nếu không thể 1:1, hãy scale kết quả có ý thức.

Bước 4 — Cắm thresholds làm SLA gate. Định nghĩa ngưỡng chấp nhận: ví dụ p(95)<800ms, http_req_failed rate<0.05. Điều này biến bài test thành pass/fail rõ ràng thay vì cảm tính.

Bước 5 — Chạy Spike Test và giám sát song song. Chạy k6 run spike.js đồng thời mở dashboard giám sát server (CPU, RAM, connection pool, queue depth, GC). Quan sát cả phía client (k6) lẫn phía server để biết điểm nghẽn nằm ở đâu.

Bước 6 — Phân tích ba pha. Chia kết quả thành: pha tăng (ramp-up), pha đỉnh (peak), pha phục hồi (recovery). So sánh error rate và latency ở pha recovery với baseline ban đầu. Nếu recovery không trở về baseline → có vấn đề kiến trúc.

Bước 7 — Chuyển sang Scalability Test. Giữ nguyên kịch bản tải, thay đổi tài nguyên theo bước (2→4→8→16 instance). Ghi lại throughput mỗi mức, vẽ đường cong. Tìm điểm đường cong bắt đầu phẳng — đó là trần mở rộng thực tế.

Bước 8 — Kết luận và hành động. Trả lời hai câu: "Với X instance, ta gánh được bao nhiêu VU an toàn?" và "Điểm nghẽn ngăn mở rộng là gì?". Đưa kết luận vào báo cáo cho team vận hành và làm cơ sở capacity planning.

Lỗi thường gặp & mẹo

Lỗi 1 — Nhầm Spike thành Stress. Nếu bạn tăng tải trong 10 phút thì đó là Load/Stress, không phải Spike. Bản chất của spike là tốc độ. Hãy để chặng ramp lên đỉnh ở mức giây, không phải phút.

Lỗi 2 — Quên đo recovery. Đây là lỗi phổ biến nhất. Nhiều người dừng test ngay khi rút khỏi đỉnh, bỏ mất phần quý giá nhất. Luôn thêm recovery stage và so sánh với baseline.

Lỗi 3 — Bị cache đánh lừa. Nếu tất cả request đều hit cùng một URL được cache, bạn đang test cache chứ không phải test hệ thống. Dùng tham số hoá dữ liệu (mỗi user gọi sản phẩm khác nhau) để tải chạm tới database và backend thật.

Lỗi 4 — Máy chạy k6 tự trở thành điểm nghẽn. Khi spike lên chục nghìn VU, chính máy load generator có thể hết CPU/băng thông và cho ra số liệu sai. Kiểm tra tài nguyên máy generator; nếu cần, dùng nhiều máy hoặc k6 Cloud (đã đề cập ở bài về cloud load testing).

Lỗi 5 — Kết luận scalability từ hai điểm dữ liệu. Chạy 2 pod rồi 4 pod là chưa đủ vẽ đường cong. Cần ít nhất 4-5 mức để thấy xu hướng phẳng dần.

Mẹo — Tận dụng ramping-arrival-rate cho spike chính xác. Thay vì điều khiển số VU (số người), hãy điều khiển số request/giây bằng executor ramping-arrival-rate. Nó mô phỏng cú spike theo throughput sát thực tế hơn, vì trong đời thực cái tăng đột ngột là số lượng yêu cầu, không phải số connection đang mở. (Chi tiết về executor types đã có ở bài k6 Scenarios.)

Mẹo — Ghép Spike Test với circuit breaker. Khi thiết kế hệ thống chịu spike, đừng cố "gánh tất". Một hệ thống tốt biết từ chối có kiểm soát (graceful degradation): trả về trang chờ, hàng đợi ảo, hoặc lỗi lịch sự — thay vì sập toàn bộ. Spike Test là nơi bạn kiểm chứng các cơ chế phòng vệ này hoạt động.

Bài tập thực hành

  • Viết một Spike Test cơ bản. Lấy một API công khai (hoặc app demo của bạn), viết kịch bản k6 với profile: baseline 50 VU (1 phút) → spike lên 1.000 VU trong 20 giây → giữ 1 phút → rút về 50 VU (2 phút recovery) → về 0. Cắm threshold p(95)<1000mshttp_req_failed rate<0.1.
  • Phân tích recovery. Chạy test ở bài 1, xuất kết quả và trả lời: error rate ở pha recovery có bằng pha baseline không? Nếu chênh lệch, hãy nêu giả thuyết về nguyên nhân (connection pool, thread, queue...).
  • Mô phỏng Scalability Test. Nếu có môi trường Docker/K8s, chạy cùng một kịch bản tải cố định qua 3 mức instance (1, 2, 4), ghi throughput mỗi mức vào bảng và tính tỷ lệ so với lý tưởng. Đường cong của bạn tuyến tính đến đâu?
  • Thử ramping-arrival-rate. Viết lại bài 1 dùng executor ramping-arrival-rate để spike theo số request/giây (ví dụ 50 rps → 800 rps trong 20s). So sánh kết quả với phiên bản điều khiển theo VU — bạn thấy khác biệt gì về hành vi hệ thống?
  • Tình huống 12/12. Giả sử traffic ngày thường của shop là 2.000 VU. Hãy thiết kế (trên giấy) một profile Spike Test cho ngày sale 12/12: chọn con số đỉnh, thời gian đạt đỉnh, thời gian recovery, và giải thích vì sao bạn chọn các con số đó.

Tóm tắt

Spike Test và Scalability Test là hai kỹ thuật giải quyết những câu hỏi mà Load Test thông thường bỏ sót. Spike Test tập trung vào tốc độ tăng tải đột ngột và — quan trọng nhất — khả năng phục hồi sau cú sốc; linh hồn của nó nằm ở recovery window, nơi phơi bày các điểm nghẽn như connection pool cạn kiệt hay queue dồn ứ. Scalability Test đo tính tuyến tính khi mở rộng, giúp bạn tìm ra điểm mà việc thêm tài nguyên trở nên vô ích (định luật Amdahl trong thực tế), từ đó tiết kiệm tiền cloud và định hướng đầu tư đúng chỗ.

Ba tình huống — vé concert, fintech trên K8s, và startup giao đồ ăn — cho thấy một thông điệp chung: sống sót qua đỉnh chưa đủ, phải phục hồi được; và có auto-scaling chưa đủ, tốc độ scaling phải theo kịp tốc độ spike. Khi bạn có thể trả lời rành mạch "traffic gấp 10 lần trong 1 phút thì hệ thống sống hay chết, và cần bao nhiêu server để cân", bạn đã nắm được thứ mà mọi team vận hành cần trước mỗi chiến dịch lớn. Trong các bài tiếp theo về đọc báo cáo test và capacity planning, chúng ta sẽ biến những con số này thành quyết định hạ tầng cụ thể.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi