사전 집계 구간
개념

집계 구간

gabury1고친 사람 github-actions[bot]

집계 구간은 끝없이 쌓이는 기록을 일정한 시간 단위로 끊어 줍니다. 끊은 토막마다 값 하나를 내면 기록 수백만 건이 그래프의 점 몇 개가 됩니다. 「평균 응답 시간」 같은 숫자는 이 구간이 정해져야 뜻이 생깁니다. 구간을 넓게 잡느냐 좁게 잡느냐에 따라 같은 기록에서 보이는 것이 달라집니다.

쉽고 빠른 이해

기록을 시간으로 잘라 토막마다 값 하나를 내는 단위입니다. 1분 구간이면 1분 동안 끝난 요청을 모아 평균 응답 시간 하나를 냅니다.

없으면 무엇이 곤란한가 — 요청 하나하나를 다 늘어놓으면 사람이 흐름을 못 읽습니다. 「언제부터 언제까지의 평균인가」가 빠진 숫자는 서로 견줄 수도 없습니다.

어떻게 도나.

  1. 시간 축을 같은 길이로 자릅니다.
  2. 기록마다 찍힌 시각을 보고 어느 토막에 드는지 정합니다.
  3. 토막마다 평균이나 최댓값 같은 계산으로 값 하나를 냅니다.

대가 — 토막 안에서 일어난 일은 값 하나에 묻힙니다. 넓게 잡으면 잠깐 튄 장애가 안 보입니다. 좁게 잡으면 점이 너무 많아져 흐름이 들쭉날쭉합니다. 그래서 방금 난 장애를 찾을 때는 좁게, 몇 주에 걸친 흐름을 볼 때는 넓게 잡습니다.

상세

이 절은 서버에 요청 기록이 시간 순으로 쌓이는 상황 하나를 놓고 갑니다. 그 기록을 어떻게 토막 내는지 먼저 봅니다. 이어서 토막의 길이가 무엇을 바꾸는지 봅니다.

가계부를 떠올려 보세요. 영수증을 한 장씩 다시 읽는 대신 한 주에 얼마, 한 달에 얼마를 적어 둡니다. 이때 한 주나 한 달이 집계 구간입니다.

집계는 여러 값을 하나로 접는 계산입니다. 개수를 세는 일, 다 더하는 일, 평균을 내는 일, 가장 큰 값을 고르는 일이 다 집계입니다.

기록이 시간과 함께 쌓이면 어느 동안의 기록을 집계할지부터 정해야 합니다. 그 「동안」이 집계 구간입니다. 구간의 길이는 폭이라고 부릅니다. 「1분 구간」은 폭이 1분인 구간입니다.

서버 모니터링이 대표입니다. 요청이 하나 끝날 때마다 걸린 시간이 기록으로 남습니다. 1분 구간으로 잡으면 1분마다 평균 응답 시간이 하나씩 나옵니다.

이렇게 서버 상태를 숫자로 잰 값을 메트릭이라고 합니다. 1분마다 나온 값은 그래프의 점 하나가 됩니다. 그 점을 시간 순으로 이은 줄이 시계열입니다.

구간 없이 요청 기록을 하나씩 늘어놓으면 하루에도 수백만 줄이 됩니다. 사람이 그 줄에서 흐름을 읽기는 어렵습니다. 구간이 빠진 숫자는 견주지도 못합니다. 어제의 평균과 지난주의 평균은 서로 다른 동안을 집계한 값이기 때문입니다.

시각으로 버킷 정하기

구간 폭으로 시간 축을 자르면 칸이 여럿 생깁니다. 그 한 칸과 거기 든 기록 묶음을 버킷이라고 부릅니다. 기록을 버킷에 나눠 담은 뒤 버킷마다 집계하면 값이 하나씩 나옵니다.

기록이 어느 버킷에 드는지는 기록에 찍힌 시각이 정합니다. 이 시각을 타임스탬프라고 합니다. 타임스탬프를 구간 폭으로 나눠 보면 그 기록이 몇 번째 버킷에 드는지 나옵니다.

아래는 1분 구간에서 2분 5초에 찍힌 기록의 버킷을 셈한 것입니다. 시각은 초로 셉니다.

Python
interval = 60              # 1분
t = 125                    # 2분 5초
bucket = t // interval     # 2
start = bucket * interval  # 120
end = start + interval     # 180

// 는 나눗셈의 몫만 남기는 연산입니다. 그래서 bucket 에는 버킷의 번호가 담깁니다. 125초 기록은 2번 버킷, 곧 120초부터 180초 앞까지에 듭니다. 같은 번호를 받은 기록끼리 한 버킷이 됩니다.

경계에 걸린 기록

딱 120초에 찍힌 기록은 앞 버킷과 뒤 버킷 사이 경계에 걸립니다. 구간은 대개 시작 시각을 넣고 끝 시각을 뺍니다. 그래서 120초 기록은 120초에 시작하는 뒤 버킷에 듭니다. 이렇게 한쪽 끝만 넣는 구간을 반열린 구간이라고 부릅니다.

경계는 보통 시계의 정각에 맞춥니다. 서버마다 켜진 시각을 기준으로 자르면 버킷이 서로 어긋납니다. 모두 정각부터 1분씩 자르면 여러 서버의 같은 버킷을 나란히 놓고 다시 집계할 수 있습니다.

하루 구간이면 어느 시간대의 자정에서 자를지도 정해야 합니다. UTC(Coordinated Universal Time, 협정 세계시) 자정과 한국 시각 자정은 아홉 시간 어긋납니다. 같은 「어제 하루」라도 기준이 다르면 담기는 기록이 달라집니다.

구간 폭이 바꾸는 것

같은 기록이라도 구간 폭에 따라 다른 그림이 나옵니다. 10분 동안 응답 시간을 쟀다고 해 보겠습니다. 그중 6분째 1분 동안만 느려졌습니다. 분마다 끝난 요청 수는 같다고 칩니다.

구간 폭 나오는 값 (밀리초) 보이는 것
1분 50 · 52 · 48 · 51 · 49 · 900 · 50 · 47 · 53 · 50 6분째의 900이 그대로 드러납니다
5분 50 · 220 뒤쪽 5분이 느렸다는 것만 남습니다
10분 135 900이 135로 묽어져 10분 내내 조금씩 높았던 것처럼 보입니다

폭을 넓힐수록 튀는 값이 주변 값과 섞여 묽어집니다. 6분째에 오래 기다린 사용자는 있었습니다. 그런데 10분 구간 그래프만 보면 1분 동안 크게 느렸는지, 10분 내내 조금씩 느렸는지 가릴 수 없습니다.

좁은 구간에도 대가가 있습니다. 점이 많아져 그래프가 들쭉날쭉해집니다. 저장할 값도 늘어납니다. 한 버킷에 든 요청이 적으면 요청 몇 건 때문에 값이 크게 출렁입니다.

그래서 폭은 무엇을 보려는지에 맞춥니다. 방금 난 장애를 찾을 때는 좁게 잡습니다. 몇 주에 걸친 흐름을 볼 때는 넓게 잡습니다.

무엇으로 집계하느냐도 같이 따집니다. 평균 대신 백분위수로 집계하면 넓은 구간에서도 튄 값이 덜 묻힙니다. 백분위수는 값을 작은 것부터 줄 세웠을 때 몇 퍼센트 지점에 있는 값입니다.

튄 값을 살리려면 높은 지점을 고릅니다. 99번째 백분위수라면 줄의 거의 끝에 선 값입니다. 느렸던 요청은 줄의 끝쪽에 서므로 높은 백분위수일수록 그 값이 덜 묽어집니다. 줄의 맨 끝을 고르는 최댓값도 같은 이유로 튄 값을 살립니다.

수집 간격과의 관계

기록이 요청마다 남지 않고 몇 초에 한 번씩 재서 남는 것도 있습니다. 메모리 사용량처럼 끊임없이 변하는 값은 정해진 간격마다 한 번씩 읽어 둡니다. 이 간격을 수집 간격이라고 합니다.

구간 폭이 수집 간격보다 좁으면 빈 버킷이 생깁니다. 15초마다 재는 값을 10초 구간으로 자른다고 해 보겠습니다. 그러면 어떤 버킷에는 측정값이 하나도 안 듭니다. 그래서 구간 폭은 한 버킷에 측정값이 여럿 들어가도록 수집 간격보다 넉넉히 잡습니다.

고정 윈도우와 슬라이딩 윈도우

지금까지 본 버킷은 서로 겹치지 않고 나란히 이어집니다. 이 방식을 고정 윈도우라고 부릅니다. 윈도우는 구간을 부르는 다른 이름입니다. 한 기록은 꼭 한 버킷에만 듭니다.

다른 방식은 구간이 시간을 따라 미끄러지는 것입니다. 「지금부터 거슬러 5분」을 1분마다 다시 잽니다. 이 방식이 슬라이딩 윈도우입니다. 구간끼리 겹치므로 한 기록이 여러 구간에 들어갑니다.

flowchart TD
    subgraph 고정["고정 윈도우 · 겹치지 않는다"]
        F1["0~5분"] --> F2["5~10분"] --> F3["10~15분"]
    end
    subgraph 슬라이딩["슬라이딩 윈도우 · 1분마다 다시 잰다"]
        S1["0~5분"] --> S2["1~6분"] --> S3["2~7분"]
    end
    고정 ~~~ 슬라이딩

두 방식 모두 폭은 5분입니다. 화살표는 그다음에 재는 구간을 가리킵니다. 위쪽은 5분마다 값이 하나 나옵니다. 아래쪽은 1분마다 값이 나옵니다.

슬라이딩 윈도우는 값이 매끈하게 이어지는 대신 계산이 더 듭니다. 겹친 기록을 구간마다 다시 집계해야 하기 때문입니다. 슬라이딩 윈도우로 낸 평균을 이어 그리면 이동 평균이 됩니다.

넓은 구간으로 다시 집계하기

오래된 기록은 촘촘하게 둘 이유가 줄어듭니다. 그래서 1분 구간 값 60개를 1시간 구간 값 하나로 다시 집계해 둡니다. 이렇게 구간을 넓혀 값 개수를 줄이는 일을 다운샘플링이라고 합니다.

flowchart TD
    A["1분 구간 · 하루에 1,440개"] -->|60개씩 다시 집계| B["1시간 구간 · 하루에 24개"]
    B -->|24개씩 다시 집계| C["하루 구간 · 하루에 1개"]

한 단계 넓힐 때마다 남는 값이 수십 분의 일로 줄어듭니다. 대신 그 단계 안의 굴곡은 되살릴 수 없습니다.

다시 집계할 때는 처음에 무엇으로 집계했는지가 걸립니다. 1분 구간의 합 60개를 더하면 1시간의 합이 그대로 나옵니다. 1분 구간의 평균 60개를 다시 평균 내면 어긋날 수 있습니다. 분마다 든 요청 수가 다르기 때문입니다.

계산마다 다시 집계할 수 있는지를 모으면 이렇습니다.

처음 집계한 값 넓은 구간으로 다시 집계하면
개수 · 합 더하면 그대로 맞습니다
최솟값 · 최댓값 다시 가장 작은 값 · 가장 큰 값을 고르면 맞습니다
평균 평균끼리는 안 맞습니다. 버킷마다 합과 개수를 남기면 맞출 수 있습니다
백분위수 버킷의 백분위수만으로는 다시 못 구합니다

그래서 다시 집계할 일이 있으면 평균 대신 합과 개수를 남겨 둡니다. 넓은 구간의 백분위수가 필요하면 값이 어느 범위에 몇 개씩 들었는지를 세어 둡니다. 이렇게 범위별로 센 표가 히스토그램입니다.

목표와 경보에 붙는 구간

구간은 그래프에만 쓰이지 않습니다. 서비스 수준 목표는 「요청 100건 중 99건 이상이 성공한다」처럼 서비스가 지킬 선을 숫자로 정한 것입니다. 이 문장에 「지난 4주 동안」 같은 구간이 붙어야 비로소 잴 수 있습니다.

구간이 정해지면 그 동안 허용되는 실패 건수도 정해집니다. 이 허용치가 오류 예산입니다. 4주 동안 요청이 1만 건이면 100건까지는 실패해도 목표를 지킵니다.

예산이 다시 차는 모양은 구간 방식에 따라 다릅니다. 구간을 「지난 30일」로 미끄러지게 잡으면 오래된 실패가 하루씩 빠져나가며 예산이 조금씩 돌아옵니다. 「이번 달」로 고정하면 달이 바뀌는 날 한꺼번에 다시 찹니다.

경보도 구간을 씁니다. 「5분 동안 평균 응답 시간이 1초를 넘으면 알린다」 같은 조건이 그렇습니다. 구간이 짧으면 잠깐 튄 값에도 경보가 울립니다. 길면 진짜 장애를 늦게 알아챕니다.

같은 것을 부르는 다른 이름

도구와 분야마다 이 구간을 다르게 부릅니다. 윈도우는 구간 자체를 가리킵니다. 집계 간격 · 스텝 · 해상도는 구간의 폭을 가리킵니다. 버킷은 앞에서 본 대로 구간 하나와 거기 든 기록 묶음입니다.

그래프 도구에서 「해상도를 낮춘다」는 말은 대개 구간을 넓힌다는 뜻입니다. 점이 성겨지는 대신 긴 기간을 한 화면에 담습니다.

관련 항목

버킷을 값 하나로 집계하는 계산

집계 · 평균 · 합계 · 최댓값 · 백분위수 · 히스토그램

구간을 나누는 방식의 갈래

고정 윈도우 · 슬라이딩 윈도 · 텀블링 윈도 · 세션 윈도 · 윈도 집계 · 이동 평균

구간을 넓혀 값을 줄이는 처리

다운샘플링 · 롤업 · 보존 기간 · 해상도

이 구간으로 잘리는 기록

버킷 · 메트릭 · 시계열 · 타임스탬프 · 수집 간격 · 샘플링 · 시계열 데이터베이스

구간을 붙여야 잴 수 있는 목표와 경보

서비스 수준 지표 · 서비스 수준 목표 · 오류 예산 · 번 레이트 · 경보 · 임계값 · 측정 창

구간 경계를 정하는 기준

반열린 구간 · 시간대 · UTC · 늦게 도착한 데이터 · 워터마크

이 구간을 다루는 상위 분야

모니터링 · 관측성 · 대시보드 · 성능 · 스트림 처리

다른 이름: 집계 간격 · aggregation interval · aggregation window