성능 측정과 병목 분석
핵심 지표
| 지표 | 의미 | 기준 |
|---|
| Latency | 요청-응답 시간 | API < 200ms |
| Throughput | 초당 처리량 | 서비스에 따라 다름 |
| p99 | 상위 1% 응답 시간 | 평균보다 중요! |
| Error Rate | 에러 비율 | < 0.1% |
병목 찾기 순서
- 모니터링: 어떤 API가 느린지 확인
- DB 쿼리 분석: slow query log, EXPLAIN
- 애플리케이션 프로파일링: CPU, 메모리, 스레드
- 인프라: 네트워크, 디스크 I/O
재기 전에 고치면 대개 틀린다
성능 문제는 짐작이 잘 안 맞습니다. 코드를 읽고 느릴 것 같은 곳을 고치면, 그 구간이 전체의
몇 퍼센트였는지 모르고 고친 것이 됩니다.
| 짐작 | 실제로 흔한 것 |
|---|
| 반복문이 느리다 | DB 를 기다리는 시간이 대부분이다 |
| 직렬화가 느리다 | 같은 질의를 여러 번 부르고 있다 |
| 서버가 부족하다 | 연결 풀 자리를 기다리고 있다 |
기다리는 시간과 일하는 시간을 나눠 재는 것이 첫 걸음입니다. CPU 사용률이 낮은데 느리면
어딘가를 기다리는 것이고, 그것은 서버를 늘려도 줄지 않습니다.
무엇을 재나
| 지표 | 무엇을 알려 주나 |
|---|
| 구간별 시간 | 어디가 긴지 |
| 호출 횟수 | 한 번이 아니라 여러 번인지 |
| 상위 백분위 | 평균에 숨은 느린 요청 |
호출 횟수가 특히 값싼 발견을 줍니다. 한 번에 20ms 인 질의가 화면 하나에서 50번 돌면
1초입니다. 그 질의를 빠르게 하는 것보다 횟수를 줄이는 것이 훨씬 크게 듣습니다.
실무 포인트
- 평균보다 p99가 더 중요 (사용자 경험에 직접 영향)
- 대부분의 병목은 DB 쿼리 → EXPLAIN 먼저 확인
- 추측하지 말고 측정부터 시작
Q.서비스 성능 병목을 어떤 순서로 찾나요?
위에서 아래로 좁힙니다. 추측으로 코드를 보기 전에 어느 구간에서 시간을 쓰는지 먼저 나눕니다.
| 순서 | 확인 | 도구 |
|---|
| 1 | 어떤 요청이 느린가 | 요청별 지연 분포, p99 |
| 2 | 그 요청의 어느 구간인가 | 분산 트레이싱, 구간 타이머 |
| 3 | 자원 중 무엇이 포화인가 | CPU, 메모리, 디스크, 네트워크, 커넥션 풀 |
| 4 | 외부 의존인가 자기 코드인가 | DB 쿼리 시간, 외부 호출 시간 |
| 5 | 코드의 어느 함수인가 | 프로파일러 |
2번을 건너뛰면 대개 헛수고가 됩니다. 느린 API 의 시간이 DB 대기인지, 외부 호출인지, 직렬화인지에 따라 손댈 곳이 완전히 다릅니다.
3번에서 자주 놓치는 것이 커넥션 풀입니다. CPU 와 메모리가 한가한데도 느리면 풀 대기를 봐야 합니다.
흔한 실수: 평균만 보고 판단하는 것. 평균이 정상이어도 p99 가 나쁘면 일부 요청이 크게 느린 것이고, 원인이 평균과 다릅니다.
Q.프로파일러 결과에서 self time과 total time을 어떻게 구분해 읽나요?
| 지표 | 의미 |
|---|
| total time | 그 함수가 시작해서 끝날 때까지의 시간. 호출한 함수들의 시간을 포함한다 |
| self time | 그 함수 자체가 쓴 시간. 하위 호출을 뺀 값 |
읽는 방법이 다릅니다.
| 상황 | 해석 |
|---|
| total 이 크고 self 는 작다 | 이 함수는 통로다. 아래를 봐야 한다 |
| total 과 self 가 모두 크다 | 이 함수 자체가 무겁다. 여기를 고친다 |
| self 가 큰 함수가 여럿 흩어져 있다 | 특정 병목이 없다. 구조를 봐야 한다 |
최상위 진입점은 항상 total 이 가장 큽니다. 그것을 병목이라고 읽으면 아무것도 못 합니다. 고칠 대상은 self 가 큰 함수입니다.
I/O 대기가 섞이면 해석이 또 달라집니다. self 가 큰데 실제로는 소켓 대기인 경우가 있어서, CPU 프로파일과 벽시계 기준 프로파일을 구분해서 봐야 합니다.
흔한 실수: 호출 횟수를 함께 보지 않는 것. 한 번에 100밀리초 걸리는 함수와 1밀리초씩 100번 불리는 함수는 self 가 같아도 대응이 다릅니다. 후자는 호출을 줄이는 쪽입니다.
Q.APM 도구로는 무엇을 보고 병목을 판단하나요?
| 보는 것 | 판단하는 것 |
|---|
| 요청별 지연 분포 | 어떤 엔드포인트가 느린가 |
| 트레이스의 구간 분해 | 그 안에서 어디에 시간을 쓰는가 |
| 느린 쿼리 목록 | DB 가 원인인가 |
| 외부 호출 시간 | 남의 서비스 때문인가 |
| 오류율과 예외 | 느린 것과 실패가 같은 원인인가 |
| 처리량과 지연의 관계 | 부하가 원인인가 |
가장 먼저 보는 것은 어떤 엔드포인트가 전체 시간을 많이 쓰는지입니다. 요청당 200밀리초짜리가 초당 1,000회면, 요청당 2초짜리가 초당 1회인 것보다 훨씬 큰 문제입니다. 지연만 보면 순서가 뒤바뀝니다.
마지막 항목이 판단을 크게 바꿉니다. 처리량이 늘면서 지연이 나빠졌다면 용량 문제이고, 처리량이 그대로인데 나빠졌다면 코드나 데이터가 바뀐 것입니다.
흔한 실수: 느린 엔드포인트 하나만 잡고 최적화하는 것. 호출량을 곱해 전체에 기여하는 시간으로 순위를 매겨야 개선 효과가 큰 곳부터 손댑니다.
먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.
읽었으면 문제로 확인해보세요
성능 최적화 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.