LabHub
배우기 러닝패스 코스

부하 테스트 · 백분위를 합치는 법 · 이론

p95 세 개의 평균은 어떤 p95 도 아니다

LabHub 에서 이어서 보기

한 줄 요약

백분위는 계산법이 하나가 아니고, 합칠 수 없다. 구간별 p95 를 평균한 값은 어떤 구간의 p95 도 전체의 p95 도 아니다.

왜 이게 필요했나

성능 회의에서 이런 표가 자주 올라온다 — 샤드 A 의 p95 는 92밀리초, 샤드 B 는 171밀리초, 샤드 C 는 818밀리초. 누군가 셋을 평균해서 "전체 p95 는 360밀리초" 라고 적는다. 그 숫자는 틀렸다. 같은 자료로 원본을 합쳐 다시 재면 253밀리초다. 이번에는 다른 사람이 트래픽 비중으로 가중 평균을 낸다 — 189밀리초. 이것도 틀렸고, 이번에는 반대 방향으로 틀렸다.

한 번 더 어긋나는 자리가 있다. 같은 표본에 같은 "p95" 를 물어도 도구마다 다른 값을 낸다. 가장 가까운 순위(nearest-rank)를 쓰는 도구는 표본에 실제로 있는 값을 돌려주고, 선형 보간을 쓰는 도구는 두 표본 사이의 값을 만들어 낸다. 표본이 20개뿐이면 그 차이가 140밀리초와 178밀리초만큼 벌어진다. 어제 쓰던 도구와 오늘 쓰는 도구가 다르면, 아무것도 바뀌지 않았는데 p95 가 나빠진 것처럼 보인다.

이 두 어긋남은 대시보드에서 조용히 일어난다. 아무도 오류를 보지 못하고, 숫자는 그럴듯하며, SLO 판정은 그 숫자로 내려진다.

어떻게 동작하나

백분위는 순서 통계량이다. 정렬한 표본에서 특정 자리의 값을 꺼내는 일이므로, "자리를 어떻게 정할 것인가" 에서 방법이 갈린다. 가장 가까운 순위는 k = ceil(q x n) 번째 값을 그대로 쓴다. 선형 보간은 h = (n - 1) x q 라는 실수 자리를 만들고 앞뒤 표본 사이를 비례로 나눈다. 표본이 많으면 둘의 차이는 사라지고, 표본이 적거나 꼬리에 외톨이 값이 있으면 크게 벌어진다.

합치지 못하는 이유는 더 근본적이다. 평균은 합과 개수로 되어 있어서 부분의 합을 더하면 전체가 되지만, 백분위는 정렬된 위치라서 부분의 위치를 더해도 전체의 위치가 되지 않는다. Prometheus 문서도 같은 말을 한다 — 미리 계산된 분위수는 서로 합칠 수 없고, 합칠 수 있는 것은 버킷이라고.

그래서 옳은 길은 둘뿐이다. 하나는 원본 관측값을 전부 한 통에 부어 다시 정렬하는 것. 정확하지만 관측값을 다 들고 다녀야 한다. 다른 하나는 히스토그램 버킷을 더하는 것. 구간마다 "50밀리초 이하 몇 건, 100밀리초 이하 몇 건" 을 세어 두면 그 개수는 그냥 더해진다. 더한 표에서 다시 백분위를 추정하면 된다. 대신 값이 아니라 버킷으로 답하므로 경계가 오차를 정한다. 경계가 성기면 추정값이 버킷 안 어딘가로 뭉개지고, 촘촘하면 정확해지는 대신 시계열이 늘어난다. HdrHistogram 같은 자료 구조가 존재하는 이유가 이 맞바꿈이다.

한 가지 더, 합친 백분위에는 지켜지는 울타리가 있다. 전체 p95 는 구간별 p95 의 최솟값과 최댓값 사이에 반드시 들어간다. 어떤 구간도 95%가 그 값 이하이면 섞어 놓아도 95%는 최댓값 이하이기 때문이다. 그래서 "합쳤더니 모든 구간보다 크게 나왔다" 는 보고는 자료가 아니라 계산을 의심해야 한다.

현장에서 만나는 모습

가장 흔한 사고는 대시보드가 구간별 p95 를 평균해 한 줄로 그려 주는 경우다. 트래픽의 10%밖에 안 되는 느린 구간이 전체 p95 를 두 배로 끌어올리고 있는데, 평균선은 그 사실을 감춘다. 반대로 가중 평균을 쓰면 느린 구간이 지워져 실제보다 낙관적인 그림이 된다. 둘 다 "평균을 쓰면 안전한 쪽으로 틀린다" 는 직관을 배신한다.

부하 시험에서도 같은 일이 벌어진다. 한 번 돌리기 부담스러운 시험을 세 번 나눠 돌리고 각 실행의 p95 를 평균해 보고서에 적는 습관이 있다. 실행마다 대상이 다르거나 부하가 다르면 그 평균은 아무것도 재지 않는다. 옳은 방법은 실행마다 원본 응답 시간을 남겨 두었다가 합쳐서 다시 계산하는 것이고, 그래서 도구의 원본 출력(hey -o csv, k6 의 결과 파일)을 버리지 않는 습관이 필요하다.

시간 축에서도 같은 함정이 있다. 1분마다 계산해 둔 p95 를 한 시간짜리 그래프로 그리면서 평균선을 얹는 대시보드가 흔한데, 그 선은 한 시간의 p95 가 아니다. 1분 p95 들의 평균은 트래픽이 적은 분과 많은 분을 같은 무게로 세기 때문에, 사고가 트래픽이 몰린 시간에 났을수록 실제보다 낮게 나온다. 한 시간의 p95 가 필요하면 그 한 시간의 버킷을 더해서 다시 구해야 한다.

도구를 바꿀 때도 확인할 것이 있다. 계산법을 문서에 적어 두는 도구가 있고 적지 않는 도구가 있다. 같은 자료로 두 도구를 한 번 돌려 보고 값이 갈리는지부터 보면, 나중에 "지난주보다 p95 가 나빠졌다" 는 보고가 도구 탓인지 서비스 탓인지 구분할 수 있다. 표본이 수만 건이면 두 방법의 차이는 사라지므로, 확인은 표본이 적은 쪽(짧은 창, 트래픽이 적은 구간)에서 해야 의미가 있다.

다음 실습에서 할 것

고정 시드로 만들어 둔 세 구간의 지연 표본을 가지고 백분위 계산법 두 가지를 손으로 구현해 값이 갈리는 것을 확인한다. 그다음 구간별 p95 의 단순 평균과 가중 평균을 전체 p95 와 견주어 두 방향으로 틀리는 것을 보고, 전체 p95 가 구간별 p95 의 최솟값과 최댓값 사이에 있다는 울타리를 확인한다. 이어서 히스토그램 버킷을 더해 합치는 방법을 구현하고 경계를 성긴 것과 촘촘한 것으로 바꿔 오차를 잰다. 마지막으로 hey 를 두 번 돌려 원본을 합친 값과 p95 를 평균한 값의 차이를 재고, 여러 실행을 올바르게 합치는 도구를 만들어 제출한다.