Zettelkasten

동시 처리 요청 수를 응답시간으로 계산했다면 응답시간을 두 번 센 것이다

·수정 1회

요약

  • 서버별로 "지금 처리 중인 요청이 몇 개인가"를 재는 지표가 없어서 응답시간 합 ÷ 시간으로 대신 구하는 경우가 있다.
  • 이 값은 요청 수 × 평균 응답시간 ÷ 시간과 같다. 로드밸런서가 요청을 똑같이 나눠서 요청 수가 같으면, 이 값은 평균 응답시간에 그냥 비례한다.
  • 그래서 "처리 중인 요청도 3배, 평균 응답시간도 3배"라고 쓰면 증거가 두 개가 아니라 하나다.

본문

어쩌다 이걸 쓰게 되나

서버가 지금 몇 개의 요청을 붙들고 있는지 직접 재는 지표는 잘 없다. 그래서 리틀의 법칙으로 대신 구한다.

처리 중인 요청 수 = 응답시간 합 ÷ 창 길이
                 = 요청 수 × 평균 응답시간 ÷ 창 길이

식을 풀어 쓰면 바로 보인다. 요청 수가 고정이면 앞의 값은 평균 응답시간에 상수를 곱한 것뿐이다.

실제로 두 서버를 비교해봤다. 로드밸런서가 요청을 똑같이 나눠주고 있던 상태다.

응답시간 비 처리 중인 요청 수 비
사례 A 5.47 5.47
사례 B 2.65 2.65

소수점 둘째 자리까지 같다. 다른 창에서는 3.1109 대 3.1105였다. 같은 수다.

"밀려서 느려졌다"의 근거로 쓸 수 없다

리틀의 법칙은 큐가 있든 없든 항상 맞는 식이다. 느린 DB를 그냥 기다리기만 해도 처리 중인 요청 수는 늘어난다. 요청이 밀려서 늘어난 것과 구분이 안 된다.

순서를 거꾸로 읽기도 쉽다. 실제로는 "서버가 느리니까 처리 중인 요청이 많아진 것"인데, 이 숫자를 보고 "요청이 쌓여서 서버가 느려졌다"고 읽게 된다.

그럼 뭘 봐야 하나

응답시간에서 계산하지 않은 값을 찾는다.

  • OS가 재는 CPU 시간. 응답시간과 상관없이 따로 측정되므로, 이 순위가 응답시간 순위와 같으면 진짜 차이가 있다는 증거가 된다.
  • 로드밸런서가 잰 시간과 앱이 잰 시간의 차이. ALB의 TargetResponseTime은 요청을 보낸 순간부터 응답이 올 때까지를 잰다. 요청이 소켓이나 프로세스 앞에서 기다렸다면 그 시간이 여기 들어간다. 앱이 잰 시간과 차이가 작으면(관찰한 사례에서는 171.0ms 대 163ms, 8ms 차이) 기다린 구간이 없다는 뜻이다.
  • 분포 모양. 요청이 밀려서 생긴 문제면 꼬리만 길어진다. p50이 1.80배, p99가 2.21배처럼 비슷하게 밀렸다면 큐가 아니라 처리 속도 자체가 느린 것이다.

남는 교훈

지표 두 개를 나란히 놓기 전에, 하나가 다른 하나를 계산해서 만든 것은 아닌지 본다. 합계나 평균을 조합해 만든 값은 원래 지표와 따로 놀지 않는 경우가 많다.

관련 노트

참고