분류 전체보기

마무리 — 전체 결과 정리 한눈에 보는 결과실험p95medianRPS에러율Baseline (VU 10, sleep 1s)12.03ms4.31ms19.760%인덱스 있음 (VU 50, sleep 1s)20.73ms7.75ms49.380%인덱스 없음 (VU 50, sleep 1s)29.04ms18.48ms49.030%Pool 테스트 (VU 100, sleep 없음)11.71ms5.38ms8,7430%해보고 나서 느낀 것들인덱스는 걸어야 한다는 거 당연히 알고 있었다. 근데 직접 재보기 전까지는 얼마나 차이 나는지 몰랐다.median이 2.4배 느려지는 걸 보고 나서야 체감이 됐다. 100만 건 기준으로 225배라는 수치는 이제 머릿속에 남아있을 것 같다.pool 테스트는 예상이 완전히 빗나갔다. 당연히 터질..
실험 3 — Connection Pool 한계connectionLimit: 10짜리 pool에 VU 100명을 sleep 없이 쏟아부으면 어떻게 될까. 당연히 터지겠지 싶었다.시나리오 코드import http from 'k6/http';import { check } from 'k6';export const options = { stages: [ { duration: '10s', target: 10 }, // pool 여유 있음 { duration: '20s', target: 10 }, { duration: '10s', target: 50 }, // pool 초과 시작 (요청 50개, 연결은 10개뿐) { duration: '20s', target: 50 }, { durat..
실험 2 — 인덱스 뺐더니 어떻게 되나같은 API, 같은 VU 수. 달라진 건 email 컬럼 인덱스 하나. 얼마나 차이가 날지 궁금했다.시나리오 코드import http from 'k6/http';import { sleep, check } from 'k6';export const options = { vus: 50, // baseline보다 5배 duration: '30s', thresholds: { http_req_duration: ['p(95) r.status === 200 }); sleep(1);}매번 랜덤 email을 쓰는 이유가 있다. 같은 값만 반복하면 MySQL이 쿼리 캐시에서 바로 꺼내줘서 인덱스를 쓰든 안 쓰든 빠르게 나온다. 그러면 실험 의미가 없어지니까 ..
실험 1 — Baseline (기준값 측정)일단 아무것도 건드리지 않은 상태에서 재봤다. VU 10명, sleep 1s로 유저 목록 조회. 이후 실험들이랑 비교할 기준선을 만드는 게 목적이다.시나리오 코드import http from 'k6/http';import { sleep, check } from 'k6';export const options = { vus: 10, duration: '30s', thresholds: { http_req_duration: ['p(95) r.status === 200 }); const users = http.get('http://localhost:3000/users?page=1&limit=20'); check(users, { 'users status..
k6로 내 API 얼마나 버티는지 직접 측정해봤다Node.js + MySQL로 만든 API에 k6로 부하를 줘봤다. 인덱스 하나 차이가 얼마나 나는지, connection pool이 어디서 터지는지 숫자로 확인한 기록.왜 했냐면"이 API 느린 것 같은데?" 라는 말을 할 때마다 답답했다. 느린 게 맞는지, 얼마나 느린지, 어디서 느린지를 아무도 몰랐다.그냥 직접 재보기로 했다.환경Node.js 20 + ExpressMySQL 8 (Docker)k6 (brew install k6 하면 끝)테스트용 유저 데이터 1만 건Fastify가 더 빠른 건 알지만, 이번 목적은 프레임워크 성능이 아니라 DB 쿼리와 connection pool 병목을 보는 거라서 Express로 충분했다.k6가 뭔데JavaScript..
5단계 — 개선 전후 비교 전체 실험을 숫자로 정리한다."빠른 것 같다"가 아니라 "p95가 얼마에서 얼마로 바뀌었다"로 말하는 것이 목표다.실험 전체 흐름① baseline → 서버의 정상 상태 측정② 인덱스 있음 → email 검색 기준점③ 인덱스 제거 → 병목 재현④ pool 한계 → sleep 없이 VU 100명, pool 포화 시도수치 비교표실험VUsleepp95avgmedRPS에러율① baseline101s12.03ms5.63ms4.31ms19.760%② 인덱스 있음501s20.73ms11.63ms7.75ms49.380%③ 인덱스 없음501s29.04ms18.63ms18.48ms49.030%④ pool 테스트0→100없음11.71ms6ms5.38ms8,7430%실험별 해..
4단계 — 병목 찾기: 인덱스 실험같은 API, 같은 VU 수(50명), 달라진 건 인덱스 하나.숫자가 어떻게 바뀌는지 본다.실험 설정엔드포인트: GET /users/search?email=userN@test.comVU: 50명, 30초변수: uq_email 인덱스 유무데이터: 10,000건쿼리:SELECT id, name, email, created_at FROM users WHERE email = ?이 쿼리가 인덱스 없이 실행되면 MySQL은 10,000건을 처음부터 끝까지 전부 훑는다(풀스캔).인덱스가 있으면 B-Tree 구조로 바로 찾아간다.결과 비교인덱스 있음 (uq_email 존재)http_req_duration: avg=11.63ms min=936µs med=7.75ms max=12..
3단계 — 결과 분석baseline 테스트를 실행했다. 실제 나온 수치를 항목별로 뜯어본다.실행 환경VU: 10명시간: 30초대상: GET /health, GET /users?page=1&limit=20seed 데이터: 10,000건전체 결과 █ THRESHOLDS http_req_duration ✓ 'p(95)항목별 해석Thresholds — 통과 기준✓ 'p(95)두 기준 모두 통과(✓).p95가 500ms 기준에 12.03ms — 기준의 2.4% 수준. 여유가 매우 크다.에러율 0% — 600개 요청 중 실패 0건.http_req_duration — 응답시간avg=5.63ms min=433µs med=4.31ms max=57.28ms p(90)=9.83ms p(95)=12.03..
2단계 — k6 설치 & 기본 시나리오 작성API가 준비됐으면 이제 부하를 줄 도구가 필요하다.이 단계에서는 k6를 설치하고, 첫 번째 부하테스트를 실행한다.왜 k6인가도구시나리오 작성결과 가독성설치JMeterGUI/XML — 복잡보통Java 필요ArtilleryYAML — 제한적보통Node 필요autocannonCLI 한 줄 — 단순보통Node 필요k6JavaScript — 자유로움좋음단독 바이너리JS로 시나리오를 짤 수 있어서 조건 분기, 로그인 후 토큰 재사용 같은 복잡한 흐름도 쉽게 표현된다.결과도 p95, p99, RPS 등 필요한 수치를 깔끔하게 출력한다.설치brew install k6핵심 개념Virtual Users (VU)VU는 브라우저 탭 하나라고 생각하면 된다.VU 10명 = 브라우저 ..
1단계 — 테스트 대상 API 준비부하테스트를 하려면 테스트 할 대상이 있어야 한다.이 단계에서는 MySQL + Express API를 세팅하고, 테스트용 데이터 1만 건을 넣는다.왜 빈 DB로 테스트하면 안 되는지?테이블이 비어있으면 MySQL이 스캔할 행이 없으니 쿼리가 비현실적으로 빠르다.실제 서비스는 수만~수백만 건의 데이터가 쌓인 상태에서 운영된다.부하테스트도 그에 맞는 환경에서 진행해야 의미 있는 숫자가 나온다.테스트 환경은 1만건의 데이터로 진행한다.Docker로 MySQL 띄우기로컬에 MySQL을 직접 설치하지 않고 Docker를 쓰는 이유:docker-compose down -v 한 줄로 DB를 완전히 초기화해 실험을 반복할 수 있다팀원이 같은 환경을 그대로 재현할 수 있다# docker..
sooyoung.c.dev
'분류 전체보기' 카테고리의 글 목록