높은 분위수
고친 사람 github-actions[bot]
높은 분위수는 가장 오래 걸린 몇 건이 어디서부터 시작되는지를 수 하나로 알려 줍니다. 평균에 묻혀 안 보이는 오래 걸린 쪽을 따로 떼어 볼 때 씁니다. 흔히 p99 같은 이름으로 부릅니다.
쉽고 빠른 이해
높은 분위수는 줄 세운 값의 뒤쪽 끝을 읽어 줍니다. 요청 천 건을 짧은 것부터 줄 세웁니다. 그리고 990번째 값을 읽습니다. 가장 오래 걸린 열 건을 뺀 나머지는 모두 그 시간 안에 끝났다는 뜻입니다.
이게 없으면 오래 걸린 쪽이 평균에 섞여 사라집니다. 사용자 한 명은 요청을 여러 번 보냅니다. 그래서 백 건에 한 건 나오는 오래 걸린 요청도 사용자 대부분이 한 번쯤은 만납니다.
- 요청마다 걸린 시간을 남깁니다
- 짧은 것부터 줄 세웁니다
- 뒤쪽 끝 가까이에 선 값을 읽습니다
대가는 둘입니다. 뒤쪽 끝은 몇 건 안 되는 값에 기대므로 건수를 많이 모아야 값이 흔들리지 않습니다. 그리고 서버 여러 대의 값을 평균 내서 합칠 수 없습니다.
사람이 기다리는 응답 시간을 약속할 때 씁니다. 합계를 셀 때나 건수가 적을 때는 쓰지 않습니다.
상세
한 해 동안 출근을 이백 번 했습니다. 대부분은 삼십 분이면 회사에 닿았습니다. 그런데 한 해를 돌아볼 때 떠오르는 날은 한 시간 넘게 걸린 두세 번입니다. 평소의 삼십 분을 아무리 늘어놓아도 그날의 지각은 지워지지 않습니다.
이백 번을 걸린 시간 순으로 줄 세우면 그 두세 번은 맨 뒤에 섭니다. 그 두세 번이 어디서부터 시작되는지를 알려 주는 수가 출근 시간의 높은 분위수입니다.
이 절은 높은 분위수가 값의 줄에서 어느 끝을 읽는지부터 봅니다. 이어서 그 값을 믿으려면 몇 건이 필요한지 따집니다. 가끔 나오는 오래 걸린 요청이 왜 사용자 대부분에게 닿는지는 짧은 계산 두 개로 따라갑니다. 끝으로 값을 합치고 잴 때 틀어지는 경우와 쓰는 때를 봅니다.
분위수의 뒤쪽 끝
분위수는 값들을 작은 것부터 줄 세운 뒤 정해진 비율 위치에 선 값입니다. 비율을 0.5 로 잡으면 줄의 한가운데 값이 나옵니다. 이 값을 중앙값이라고 부릅니다.
비율을 0.99 처럼 1 에 가깝게 잡으면 줄의 뒤쪽 끝 가까이에 선 값이 나옵니다. 이렇게 뒤쪽 끝을 읽는 분위수를 높은 분위수라고 부릅니다. 가장 큰 값들이 어디서부터 시작되는지를 알려 주는 수입니다.
비율을 백분율로 적은 분위수가 백분위수입니다. 0.99 위치의 값은 아흔아홉 번째 백분위수입니다. 줄여서 p99 로 적습니다. p 는 백분위수를 뜻하는 영어 percentile 의 첫 글자입니다.
몇부터 높다고 부르는지 정해진 선은 없습니다. p95 나 p99 에서 시작해 그 위를 가리키는 일이 많습니다.
요청 천 건의 줄
성능 이야기에서는 대개 응답 시간을 놓고 높은 분위수를 읽습니다. 응답 시간은 요청을 보낸 때부터 답을 다 받은 때까지 걸린 시간입니다. 요청 하나에 값 하나가 나옵니다.
요청 천 건의 응답 시간을 짧은 것부터 줄 세웠다고 하겠습니다. p99 는 990번째 값입니다. p99.9 는 999번째 값입니다.
flowchart TD
subgraph 줄["짧은 것부터 줄 세운 요청 천 건"]
A["1~989번째 · 989건"]
P99["990번째 값 = p99"]
B["991~998번째 · 8건"]
P999["999번째 값 = p99.9"]
C["1000번째 · 1건"]
A --> P99 --> B --> P999 --> C
end
그림에서 p99 보다 뒤에 선 요청은 열 건입니다. p99.9 보다 뒤에는 한 건뿐입니다. 뒤쪽 끝으로 갈수록 그 값을 떠받치는 요청이 빠르게 줄어듭니다.
9 를 더 붙이는 표기
p99 뒤에 9 를 더 붙여 p99.9 · p99.99 처럼 더 깊은 끝을 가리킵니다. p99.9 는 점을 빼고 p999 로 적기도 합니다.
9 가 하나 늘 때마다 그 값을 넘는 요청의 비율은 열 배 줄어듭니다. 요청 백만 건을 놓고 세면 아래와 같습니다.
| 표기 | 이 값을 넘는 요청의 비율 | 백만 건 가운데 넘는 건수 |
|---|---|---|
| p99 | 백 건에 한 건 | 만 건 |
| p99.9 | 천 건에 한 건 | 천 건 |
| p99.99 | 만 건에 한 건 | 백 건 |
표의 마지막 열이 이 값의 무게를 보여 줍니다. 백만 건을 쟀어도 p99.99 를 떠받치는 요청은 백 건입니다.
값을 믿는 데 드는 건수
높은 분위수는 적은 건수에 기댑니다. 백 건만 재서 p99 를 읽으면 그 값보다 뒤에 선 요청은 한 건입니다. 그 한 건이 우연히 오래 걸렸는지에 따라 p99 가 크게 흔들립니다.
뒤에 선 요청이 많을수록 값은 덜 흔들립니다. p99 를 믿을 만큼 모은 건수로 p99.9 를 읽으면 뒤에 선 요청이 열 배 적습니다. 그래서 9 를 하나 더 붙이면 같은 만큼 안정된 값을 얻는 데 열 배의 요청이 듭니다.
높은 분위수를 적을 때는 몇 건을 재서 나온 값인지를 같이 적습니다. 건수가 모자라면 더 낮은 분위수를 보거나 재는 기간을 늘려 건수를 모읍니다.
오래 걸린 요청을 만나는 사람의 수
백 건에 한 건만 오래 걸린다면 대수롭지 않게 들립니다. 그런데 사용자 한 명은 요청을 한 번만 보내지 않습니다. 화면 몇 장을 넘기는 동안에도 요청이 수십 번 오갑니다.
사용자가 요청을 백 번 보낸다고 하겠습니다. 요청마다 p99 를 넘을 확률은 백에 하나입니다. 요청끼리 서로 영향이 없다고 치면 백 번 중 한 번이라도 넘을 확률은 이렇게 셉니다.
p = 0.01 # p99 를 넘을 확률
1 - (1 - p) ** 100 # 0.634
(1 - p) ** 100 은 백 번 모두 p99 안에 끝날 확률입니다. 1 에서 이 값을 빼면 적어도 한 번은 넘을
확률이 됩니다. 사용자 셋 중 둘 가까이가 p99 를 넘는 요청을 한 번은 겪습니다.
p99 는 요청 백 건 가운데 한 건의 값입니다. 사용자 백 명 가운데 한 명의 경험이 아닙니다.
여러 곳을 부르는 요청
화면 하나를 그리려고 뒤에서 여러 서비스를 한꺼번에 부르는 구조가 있습니다. 한 요청이 여러 호출로 퍼지는 이 모양을 팬아웃이라고 부릅니다. 부른 답이 모두 와야 화면이 뜨므로 화면은 가장 늦게 온 답을 기다립니다. 아래 그림은 호출이 백 개인 경우입니다.
flowchart TD
A["화면 요청 한 건"] --> B1["호출 1"]
A --> B2["호출 2"]
A --> B3["호출 3"]
A --> BN["나머지 호출 97개"]
B1 --> C["백 개가 다 와야 화면이 뜬다"]
B2 --> C
B3 --> C
BN --> C
화면이 어떤 시간 안에 뜨려면 호출 백 개가 모두 그 시간 안에 끝나야 합니다. 이번에는 거꾸로 묻습니다. 화면의 절반이 그 시간 안에 뜨려면 호출 하나는 얼마나 자주 그 안에 끝나야 할까요.
호출 백 개가 모두 같은 확률로 그 시간 안에 끝난다고 치겠습니다. 호출끼리는 서로 영향을 주지 않는다고 봅니다. 그러면 호출 하나가 그 시간 안에 끝날 확률을 백 번 곱한 값이 0.5 여야 합니다. 그 확률을 거꾸로 셈하면 이렇습니다.
0.5 ** (1 / 100) # 0.9931
호출 하나가 0.9931 의 확률로 그 시간 안에 끝난다는 말은 그 시간이 호출의 p99.3 쯤이라는 뜻입니다. 화면 절반이 그 시간 안에 뜬다는 말은 그 시간이 화면의 중앙값이라는 뜻입니다. 그래서 호출의 p99.3 이 화면의 중앙값이 됩니다. 뒤에서 불리는 서비스에게는 높은 분위수가 사용자의 평소 경험입니다.
최댓값과의 차이
뒤쪽 끝을 보려면 최댓값을 보면 된다고 여기기 쉽습니다. 최댓값은 줄의 맨 끝에 선 한 건입니다. 이 값은 오래 잴수록 커지기만 합니다. 요청을 더 모으면 더 오래 걸린 한 건이 끼어들 기회도 늘기 때문입니다.
높은 분위수는 비율로 위치를 잡습니다. 건수가 늘면 뒤에 서는 요청도 같이 늘어서 값이 한 값 근처로 모입니다. 그래서 재는 기간이 다른 두 측정을 견줄 수 있습니다.
최댓값도 버리지 않습니다. 가장 오래 걸린 한 건이 무엇이었는지 찾아 원인을 볼 때 씁니다.
합치는 법
높은 분위수는 평균 내서 합칠 수 없습니다. 서버 열 대의 p99 를 평균 낸 값은 전체 요청의 p99 가 아닙니다. 1분마다 구한 p99 예순 개를 평균 내도 한 시간의 p99 가 나오지 않습니다.
분위수는 줄 세운 위치의 값입니다. 줄이 바뀌면 같은 위치에 다른 값이 섭니다. 그래서 합치려면 값을 다시 모아 줄을 세워야 합니다.
요청을 전부 남기기 어려우면 값의 구간마다 몇 건인지만 세어 둡니다. 이렇게 센 표가 히스토그램입니다. 구간별 건수는 서버끼리 더할 수 있습니다. 더한 건수에서 분위수를 다시 읽습니다.
구간이 정하는 정밀도
히스토그램에서 읽은 p99 는 「1초와 2.5초 사이 어딘가」처럼 구간 하나로만 좁혀집니다. 높은 분위수는 값이 드문드문 흩어진 뒤쪽 끝에 있습니다. 그래서 뒤쪽 구간이 넓으면 오차가 커집니다.
구간 폭을 값에 비례해 넓히면 오차를 값의 몇 퍼센트 안으로 묶을 수 있습니다. 이렇게 적은 메모리로 분위수를 어림하는 방법들을 근사 분위수라고 부릅니다.
재는 쪽이 값을 낮추는 경우
높은 분위수는 요청이 밀린 순간을 빠짐없이 잡았는지에 달려 있습니다. 그런 순간을 덜 잡으면 값이 낮게 나옵니다.
부하 테스트 도구가 앞 요청의 답을 받은 뒤에야 다음 요청을 보낸다고 하겠습니다. 서버가 5초 동안 멈추면 도구도 5초 동안 요청을 못 보냅니다. 그 5초 동안 보냈어야 할 요청은 기록에 없습니다. 오래 걸린 한 건만 남습니다. 이 탓에 높은 분위수가 낮게 나오는 현상을 조율된 누락이라고 부릅니다.
어디서 재는지도 값을 바꿉니다. 서버 안에서 잰 시간에는 서버 앞 대기열에서 차례를 기다린 시간이 빠집니다. 이 기다림은 붐비는 순간에만 길어집니다. 그래서 요청을 보낸 쪽에서 잰 값과의 차이는 평균보다 높은 분위수에서 크게 벌어집니다.
쓰는 때와 안 쓰는 때
사람이 기다리는 시간을 약속할 때 씁니다. 「요청 백 건 중 아흔아홉 건이 0.3초 안에 끝난다」처럼 비율과 시간을 짝지어 서비스 수준 목표를 적습니다. 평균으로 적으면 오래 걸린 요청이 적지 않아도 약속이 지켜진 것처럼 보일 수 있습니다.
합계나 용량을 셀 때는 쓰지 않습니다. 평균에 건수를 곱하면 합계가 나옵니다. 높은 분위수에는 그런 성질이 없습니다.
건수가 적을 때도 쓰지 않습니다. 뒤에 선 요청이 몇 건 안 되면 값이 흔들려서 어제와 오늘을 견줄 수 없습니다. 그럴 때는 오래 걸린 요청 몇 건을 하나하나 들여다봅니다.
사람이 기다리지 않는 일도 있습니다. 밤새 도는 배치 처리는 한 건의 기다림보다 전체가 언제 끝나느냐를 봅니다. 그때는 처리량을 먼저 봅니다.
관련 항목
높은 분위수가 속하는 통계량 무리
분위수 · 백분위수 · 중앙값 · 사분위수 · 십분위수 · 최댓값 · 평균 · 요약 통계
높은 분위수로 읽는 값의 모양
분포 · 긴 꼬리 · 꼬리 지연 · 이상치 · 치우침 · 누적 분포 함수
높은 분위수로 재는 성능 지표
응답 시간 · 지연 · 처리량 · 오류율 · 초당 요청 수 · 성능
높은 분위수를 셈하거나 어림하는 방법
정렬 · 선택 알고리즘 · 히스토그램 · 근사 분위수 · t-digest · HdrHistogram · DDSketch
높은 분위수 값을 흔드는 요인
표본 크기 · 측정 창 · 조율된 누락 · 팬아웃 · 대기열 · 가비지 컬렉션 정지
높은 분위수로 약속을 적는 운영 문서
서비스 수준 목표 · 서비스 수준 지표 · 서비스 수준 계약 · 에러 예산 · 경보
높은 분위수를 리포트로 내는 도구
다른 이름: high percentile · high quantile · tail percentile · 높은 백분위수 · 상위 분위수