P95와 P99가 평균 응답 시간보다 중요한 이유

· 약 6분 읽기 · 조회 1 #성능#모니터링#SLO#응답 시간#성능 시험

"평균 응답 시간 0.3초"라는 보고서는 사용자 100명 중 5명이 4초를 기다렸다는 사실을 숨길 수 있다. 응답 시간 분포는 오른쪽으로 긴 꼬리를 갖고, 장애와 민원은 그 꼬리에서 나온다. 이 글은 평균이 왜 사용자 경험을 대변하지 못하는지, P95·P99를 어떻게 재고 합치고 목표로 삼아야 하는지를 정리한 것이다.

매표 창구 앞 긴 줄에서 대부분은 빠르게 지나가지만 끝쪽 몇 사람은 짐을 잔뜩 든 채 훨씬 오래 기다리고, 천장에는 커다란 시계가 걸린 역 대합실

평균은 꼬리를 숨긴다

요청 100건 중 95건이 0.1초, 5건이 4초 걸렸다고 하자.

지표 값 읽는 법
평균 0.295초 모두가 0.3초 안에 끝난 것처럼 보인다
P50(중앙값) 0.1초 절반은 0.1초 안에 끝났다
P95 0.1~4초 경계 95%까지는 빠르다(여기서 꼬리가 시작)
P99 4초 100명 중 1명은 4초를 기다렸다

평균은 극단값 몇 개에 끌려가면서도 그 극단값이 얼마나 나쁜지는 보여 주지 않는다. 퍼센타일 Pn은 "요청의 n%가 이 시간 안에 끝났다"는 뜻이라, 느린 쪽에 있는 사용자가 실제로 겪은 시간을 그대로 드러낸다.

대부분의 요청이 짧은 시간에 몰리고 오른쪽으로 긴 꼬리가 늘어진 응답 시간 분포에서 중앙값, 평균, P95, P99가 각각 어디에 놓이는지 보여 주는 예시 그래프

사용자는 꼬리를 더 자주 만난다

화면 하나는 보통 여러 API를 부르고, API 하나는 다시 여러 내부 호출을 한다. 호출마다 1%의 확률로 P99만큼 느리다면, 호출이 많을수록 적어도 하나가 느릴 확률이 커진다.

화면 하나가 기다리는 호출 수 적어도 하나가 P99 이상일 확률
1 1%
10 약 9.6%
50 약 39.5%
100 약 63.4%

계산은 1 − 0.99^N이다. 내부 호출이 50번인 화면이라면 사용자 열 명 중 네 명이 P99의 지연을 겪는다. 대규모 서비스에서 꼬리 지연이 전체 경험을 지배한다는 이 현상은 Google의 Dean과 Barroso가 "The Tail at Scale"(2013)에서 정리했다. P99는 드문 사용자의 이야기가 아니다.

꼬리는 어디서 생기나

  • 자원 대기: 커넥션 풀, 스레드 풀, 락이 잠깐 비지 않을 때
  • GC 멈춤: JVM의 긴 가비지 컬렉션
  • 캐시 miss: 평소엔 캐시에서, 가끔 DB에서
  • 외부 연계: 다른 기관 API의 들쭉날쭉한 응답
  • 배치·백업과 겹친 시간대: 같은 DB를 쓰는 무거운 작업
  • 큰 요청: 특정 사용자의 데이터가 유난히 많은 경우

평균은 이런 원인이 생겨도 거의 움직이지 않는다. P99가 먼저 움직인다. 그래서 장애의 조기 경보로도 퍼센타일이 낫다.

퍼센타일을 제대로 재는 법

평균을 평균 내지 않듯, 퍼센타일도 평균 내지 않는다. 서버 4대의 P99를 각각 구해 평균하면 전체 P99가 아니다. 서버마다 히스토그램(구간별 요청 수)을 모으고, 합친 히스토그램에서 퍼센타일을 계산해야 한다. Prometheus의 히스토그램과 histogram_quantile, HdrHistogram 같은 도구가 이 방식을 쓴다.

# 5분 동안 모든 인스턴스의 요청을 합친 P99
histogram_quantile(0.99, sum by (le) (rate(http_server_requests_seconds_bucket[5m])))

측정 창을 정한다. 1분 P99와 1일 P99는 다른 숫자다. 모니터링은 짧은 창(1~5분), 목표 관리는 긴 창(1주·1개월)으로 본다.

부하 도구의 함정을 안다. 많은 부하 도구는 응답이 올 때까지 다음 요청을 보내지 않는다. 서버가 멈춘 동안 요청 자체가 덜 보내져, 느린 순간이 통계에서 빠진다(Gil Tene가 말한 coordinated omission). 일정한 도착률로 요청을 보내는 방식으로 시험하거나, 이 보정을 하는 도구를 쓴다.

표본 수를 확인한다. 요청이 200건이면 P99는 가장 느린 두 건으로 정해진다. 퍼센타일은 충분한 요청 수가 있을 때만 의미가 있다.

목표(SLO)는 퍼센타일로 쓴다

나쁜 목표 좋은 목표
평균 응답 시간 1초 이내 조회 API 응답 P95 1초 이내, P99 3초 이내(5분 창), 오류율 0.1% 미만
빠른 응답 피크 시간 동시 사용자 2,000명에서 위 기준 유지
  • P95는 "대부분 사용자의 체감", P99는 "꼬리 관리"를 맡긴다.
  • 화면·API 성격별로 다르게 둔다. 로그인과 대용량 보고서를 같은 기준으로 묶지 않는다.
  • 오류율을 함께 둔다. 타임아웃으로 실패한 요청을 빼면 퍼센타일이 좋아 보인다.

대시보드에 무엇을 올릴까

  • 처리량, 오류율, P50·P95·P99를 같은 시간축에 둔다.
  • 평균은 빼거나 작게 둔다. 평균이 크게 보이면 의사결정이 평균을 따라간다.
  • P99 경보는 짧은 창의 일시적 튐에 울리지 않게 "5분 연속 기준 초과"처럼 지속 조건을 붙인다.
  • 느린 요청 몇 건은 추적(트레이스)으로 바로 열어 볼 수 있게 연결한다.

자주 하는 실수

  • 보고서에 평균만 쓴다. 꼬리의 민원과 장애가 숫자에서 사라진다.
  • 서버별 P99를 평균한다. 실제 P99보다 낮게 나온다.
  • 실패한 요청을 빼고 퍼센타일을 계산한다. 가장 나쁜 경험이 통계에서 지워진다.
  • 표본이 적은데 P99를 말한다. 한두 건이 숫자를 결정한다.
  • 모든 API에 같은 목표를 건다. 무거운 기능은 늘 실패하고, 가벼운 기능의 퇴보는 안 보인다.

점검표

  • 성능 목표가 P95·P99와 오류율로 적혀 있다
  • 목표마다 측정 창과 부하 조건이 정해져 있다
  • 퍼센타일은 히스토그램을 합쳐서 계산한다
  • 실패한 요청도 응답 시간 통계에 들어간다
  • 부하 시험이 coordinated omission을 고려한다
  • 대시보드에 P50·P95·P99와 처리량·오류율이 함께 있다
  • P99 경보에 지속 조건이 있다
  • 느린 요청을 트레이스로 바로 열어 볼 수 있다

평균은 시스템이 대체로 어떤지를 말하고, P95·P99는 사용자가 실제로 무엇을 겪는지를 말한다. 목표와 보고서와 경보를 퍼센타일로 바꾸면, 숨어 있던 꼬리가 고칠 수 있는 문제로 드러난다.

댓글 0개

아직 댓글이 없습니다. 첫 댓글을 남겨보세요.

댓글 쓰기

← 목록으로 돌아가기