지연 시간과 처리량
백분위의 의미
| 지표 | 의미 |
|---|
| p50 | 요청 절반이 이보다 빠름 (중앙값) |
| p95 | 20건 중 1건은 이보다 느림 |
| p99 | 100건 중 1건은 이보다 느림 |
| max | 최악 1건. 노이즈가 크다 |
평균이 위험한 이유
응답시간(ms) 10건
50 50 50 50 50 50 50 50 50 4000
평균 445 <- 아무도 겪지 않은 값
p50 50
p90 4000 <- 실제 불만이 나오는 값
둘은 같이 좋아지지 않는다
처리량을 올리려고 한 번에 많이 묶으면 각 요청의 지연이 늘어납니다. 지연을 줄이려고 바로바로
보내면 묶는 이득이 사라집니다.
| 무엇을 하면 | 처리량 | 지연 |
|---|
| 여러 개를 모아 한 번에 처리 | 오른다 | 길어진다 |
| 오는 대로 바로 처리 | 낮다 | 짧다 |
| 일꾼을 늘린다 | 오른다 | 대기가 줄어 짧아진다 |
셋째만 둘을 함께 좋게 합니다. 그런데 한계까지만 그렇습니다. 그 뒤로는 일꾼을 늘려도
처리량이 안 오르고 대기만 늘어납니다.
목표를 어떻게 적나
"빠르게" 는 목표가 아닙니다. 세 가지가 함께 있어야 확인할 수 있는 목표가 됩니다.
어느 지점에서 재나: 사용자 화면인가 서버 안인가
어느 백분위인가: 상위 1퍼센트인가 절반인가
어느 부하에서인가: 초당 100인가 1만인가
부하가 빠진 목표가 가장 흔합니다. 한가할 때 50ms 인 것은 아무 약속이 아닙니다.
실무 포인트
- 한 화면이 내부 API 10개를 부르면 그중 하나가 꼬리 지연에 걸릴 확률이 커진다. 팬아웃이 클수록 p99가 체감을 지배한다
- 처리량과 지연은 독립이 아니다. 동시성은 대략 처리량 곱하기 평균 지연이므로, 지연이 2배면 같은 처리량에 커넥션과 스레드가 2배 필요하다
- 자원이 포화에 가까워지면 지연은 선형이 아니라 급격히 튄다. 여유를 남긴다
- 백분위는 평균할 수 없다. 서버별 p99를 평균 내지 말고 원본 분포에서 계산한다
Q.평균 응답시간 대신 p99를 보는 이유는 무엇인가요?
평균은 느린 요청을 숨깁니다. 그리고 사용자는 평균을 경험하지 않습니다.
응답이 100밀리초인 요청 99개와 10초인 요청 1개가 있으면 평균은 199밀리초입니다. 지표는 건강해 보이지만 100명 중 1명은 10초를 기다립니다.
| 지표 | 보여주는 것 |
|---|
| 평균 | 전체 경향. 이상값에 흔들린다 |
| p50 | 절반의 사용자가 겪는 시간 |
| p99 | 100명 중 가장 느린 1명의 시간 |
| 최댓값 | 한 건의 이상값. 판단에 쓰기 어렵다 |
한 화면이 여러 API 를 부르면 더 커집니다. API 10개를 부르는 화면이라면 각 API 의 p99 가 1%여도 화면 하나가 느릴 확률은 10%에 가깝습니다. 개별 지표가 좋아 보여도 사용자 체감은 그렇지 않은 이유입니다.
흔한 실수: p99 를 목표로만 두고 p50 을 버리는 것. p50 이 나쁘면 전원이 느린 것이고 원인이 다릅니다. 둘을 함께 봐야 어떤 문제인지 구분됩니다.
Q.p99가 나쁜데 p50은 정상일 때 어떻게 원인을 찾나요?
전체가 느린 것이 아니라 일부 요청만 느린 것입니다. 그 일부의 공통점을 찾습니다.
| 가설 | 확인 방법 |
|---|
| 특정 데이터가 크다 | 느린 요청의 파라미터를 본다. 목록 크기, 기간 범위 |
| 캐시 미스 | 히트와 미스로 나눠 지연을 비교한다 |
| 특정 서버 | 서버별로 나눠 본다. 한 대만 나쁠 수 있다 |
| 잠금 경합 | 대기 시간과 동시 요청 수의 관계를 본다 |
| 커넥션 풀 대기 | 풀 대기 시간을 별도로 잰다 |
| GC 멈춤 | GC 로그의 멈춤 시간과 느린 요청 시각을 맞춰본다 |
| 콜드 스타트 | 배포 직후나 오래 유휴 후에 몰리는지 본다 |
순서는 분해하기 쉬운 것부터입니다. 서버별, 엔드포인트별, 사용자별로 나눠 보면 대개 한쪽에 몰려 있습니다. 몰려 있지 않고 고르게 흩어져 있으면 GC 나 잠금처럼 시점 의존적인 원인입니다.
흔한 실수: 평균 자원 사용률을 보고 여유 있다고 판단하는 것. p99 문제는 순간적인 경합이라 1분 평균에서는 보이지 않습니다. 짧은 간격의 지표나 트레이스를 봐야 합니다.
Q.처리량을 높였는데 지연이 나빠지는 이유는?
자원이 포화에 가까워지면 대기 줄이 생기기 때문입니다. 대기 시간은 이용률이 100%에 가까워질수록 급격히 늘어납니다.
| 이용률 | 대기 시간의 경향 |
|---|
| 50% | 처리 시간과 비슷한 수준 |
| 80% | 몇 배로 늘어난다 |
| 95% | 급격히 커진다 |
| 100% 근처 | 줄이 무한히 길어진다 |
그래서 처리량과 지연은 함께 좋아지지 않습니다. 처리량을 끝까지 끌어올리면 지연은 반드시 나빠집니다.
| 나타나는 자리 | 예 |
|---|
| 커넥션 풀 | 풀이 꽉 차 대기가 생긴다 |
| 스레드 | 실행 가능한 스레드가 코어보다 많아 대기한다 |
| 디스크와 네트워크 | 큐가 쌓인다 |
| DB | 잠금 경합과 커넥션 한계 |
대응은 이용률에 여유를 두는 것입니다. 목표를 60에서 70% 정도로 잡고 그 이상이면 증설합니다. 그리고 처리량 목표와 지연 목표를 함께 정합니다.
흔한 실수: 부하 테스트에서 초당 처리량 최대치만 보고 용량을 정하는 것. 그 지점은 이미 지연이 무너진 곳입니다. 지연 목표를 만족하는 최대 처리량이 실제 용량입니다.
Q.서버 여러 대의 p99를 하나의 값으로 어떻게 집계하나요?
평균하면 안 됩니다. 백분위수는 평균할 수 없는 값입니다.
서버 A 의 p99 가 100밀리초, B 가 500밀리초라고 해서 전체 p99 가 300밀리초인 것이 아닙니다. 전체 p99 는 두 서버의 요청을 모두 합친 분포에서 다시 계산해야 합니다.
| 방법 | 정확도 | 비용 |
|---|
| 원본 지연을 모두 모아 계산 | 정확하다 | 저장과 계산 비용이 크다 |
| 히스토그램을 합산 | 버킷 폭만큼의 오차 | 실용적. 대부분 이 방식 |
| 각 서버 p99 의 최댓값 | 보수적. 과대평가 | 싸다 |
| 각 서버 p99 의 평균 | 틀렸다 | 쓰지 않는다 |
실무에서는 두 번째를 씁니다. 각 서버가 구간별 건수를 히스토그램으로 보고하면 그것을 더해 전체 분포를 만들 수 있습니다. 이것이 합산 가능한 형태로 지표를 남기는 이유입니다.
흔한 실수: 대시보드가 서버별 p99 를 평균해서 보여주는데 그것을 전체 p99 로 읽는 것. 한 서버만 나쁠 때 그 값이 희석되어 문제가 감춰집니다.
먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.
읽었으면 문제로 확인해보세요
성능 최적화 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.