Facebook 엔지니어들이 2015년 VLDB(Very Large Data Bases 학회)에 발표한 Gorilla 논문은 시계열 데이터 포인트 하나를 저장하는 데 원래 16바이트가 필요했던 것을, 평균 1.37바이트까지 줄였다고 보고합니다. 12배에 가까운 압축률이에요. 그런데 이 숫자를 그냥 “옛날 논문 얘기”로 흘려보내기엔 아까운 이유가 있어요 — 지금 홈랩에서 흔히 쓰는 Prometheus의 TSDB(시계열 데이터베이스)가 바로 이 압축 아이디어를 그대로 채택해서 돌아가고 있고, 그 덕분에 리텐션(retention, 보관 기간)을 며칠로 잡을지 몇 개월로 잡을지에 따라 디스크 사용량이 얼마나 달라지는지를 실제로 계산할 수 있거든요. 그렇다면 Gorilla 논문의 압축률을 근거로 Prometheus 리텐션 기간별 디스크 용량을 실제로 어떻게 계산할까요?
Gorilla 논문이 밝힌 압축률은 정확히 어느 정도인가?
Gorilla 논문은 압축하지 않은 시계열 데이터 포인트 하나(타임스탬프 8바이트 + 값 8바이트)의 크기를 16바이트로 잡고, 이를 평균 1.37바이트까지 줄였다고 밝혀요. 원문 그대로 인용하면 “compress each by series down from 16 bytes to an average of 1.37 bytes, a 12x reduction in size”라고 나와 있어요. 이 압축은 두 가지 기법의 조합으로 이뤄지는데, 타임스탬프는 이전 값과의 차이의 차이(delta-of-delta)를 계산해 대부분의 경우 아주 작은 숫자로 표현하고, 값은 이전 값과 XOR 연산을 한 뒤 그 결과에서 의미 있는 비트만 남기는 방식이에요.
논문에 실린 실측 분포도 꽤 구체적이에요. Facebook 프로덕션 데이터셋 기준으로 전체 타임스탬프의 약 96%가 단 1비트로 압축됐고, 값의 경우 약 51%가 직전 값과 동일해 역시 1비트로 압축됐어요. 나머지 값들도 평균 26.6비트(약 30%) 또는 그보다 조금 더 큰 비트 수(약 19%)로 압축되는 식이라, 전체 평균이 1.37바이트에 수렴해요. 이 값은 2시간 단위 블록으로 압축했을 때의 결과이고, 논문은 블록 크기를 2시간보다 더 키워도 압축률 개선 효과가 거의 없다는 점도 함께 확인했어요. Facebook은 이 압축 덕분에 초당 1,200만 개, 하루로 치면 1조 개가 넘는 데이터 포인트를 메모리에 올려두고 서비스할 수 있었다고 밝히고 있어요.
Prometheus 공식 문서는 디스크 용량을 어떤 공식으로 계산하나?
Prometheus 공식 문서는 필요한 디스크 용량을 needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample라는 단순한 곱셈 공식으로 안내해요. 세 항목 중 리텐션 기간(초)과 초당 수집 샘플 수는 운영자가 스크레이프 간격과 시계열 개수로 직접 계산할 수 있는 값이고, 남은 변수인 샘플당 바이트 수(bytes_per_sample)에 대해 공식 문서는 “Prometheus stores an average of only 1-2 bytes per sample(평균 1~2바이트)“라고 명시해요.
여기서 Grafana + Prometheus 모니터링 스택 글에서 다뤘던 --storage.tsdb.retention.time 옵션이 바로 이 공식의 첫 번째 변수를 결정하는 설정이에요. 리텐션을 30일로 잡을지 1년으로 잡을지가 디스크 사용량을 그대로 좌우한다는 뜻이라, 이 공식을 모르고 리텐션 값을 정하면 나중에 디스크가 예상보다 훨씬 빨리 차오르는 상황을 겪게 돼요.
왜 Prometheus의 샘플 크기가 Gorilla의 압축률과 같은 범위에 들어올까?
Prometheus TSDB가 1~2바이트/샘플이라는 결과를 내는 이유는 Gorilla 논문의 압축 아이디어를 사실상 그대로 구현했기 때문이에요. Prometheus 저장소 엔진은 타임스탬프를 delta-of-delta 방식으로, 값을 XOR 방식으로 압축하는 청크(chunk) 구조를 쓰는데, 이는 Gorilla 논문 4장에서 설명하는 압축 알고리즘과 같은 원리예요. 이 XOR 비트 인코딩 자체가 왜 이렇게까지 효과적인지는 Gorilla의 XOR 압축 원리에서, Prometheus TSDB 구현이 Gorilla 원본과 구체적으로 어떻게 다른지는 Prometheus TSDB와 Gorilla 비교에서 더 깊게 다루고 있으니, 여기서는 “왜 두 수치가 비슷한 범위에 있는가”라는 결과에만 집중할게요.
결론부터 말하면 두 시스템 모두 시계열 데이터의 핵심 특성, 즉 연속된 값이 거의 비슷하거나 동일하다는 점을 파고들어 압축한다는 공통 설계에서 비슷한 수치가 나오는 거예요. 서버 CPU 사용률이나 메모리 점유율 같은 지표는 15초 간격으로 봤을 때 직전 값과 크게 다르지 않은 경우가 대부분이라, 이런 워크로드에서 XOR·delta-of-delta 압축이 특히 잘 먹혀요.
홈랩 시나리오로 직접 계산해보면 얼마나 차이 날까?
리텐션 기간에 따른 디스크 사용량은 시계열 개수와 스크레이프 간격만 정하면 공식에 바로 대입해볼 수 있어요. 예를 들어 홈서버 3대에 각각 node_exporter를 설치했고, 대에서 노출되는 시계열이 800개 정도(node_exporter 기본 지표 기준으로 흔히 나오는 규모)라고 가정해볼게요. 스크레이프 간격을 15초로 두면 초당 수집 샘플 수는 (800개 × 3대) ÷ 15초 = 160샘플/초가 나와요. 이 값을 공식에 그대로 대입하면 리텐션 기간별 디스크 사용량은 아래와 같이 계산돼요(Gorilla의 1.37바이트/샘플과 Prometheus 문서가 제시하는 상한값인 2바이트/샘플 두 기준으로 각각 계산).
- 15일 보관: 약 271MiB(1.37바이트 기준) ~ 396MiB(2바이트 기준)
- 30일 보관: 약 542MiB ~ 791MiB
- 90일 보관: 약 1.6GiB ~ 2.3GiB
- 1년 보관: 약 6.4GiB ~ 9.4GiB
숫자만 놓고 보면 홈랩 규모에서는 1년치를 보관해도 디스크 용량 자체는 그리 부담스럽지 않다는 걸 알 수 있어요. 여기서 끝이 아니에요 — 이 계산은 어디까지나 시계열 개수가 리텐션 기간 내내 일정하게 유지된다는 전제 위에서만 성립해요.
리텐션을 늘렸을 때 계산이 틀어지는 진짜 이유는 무엇일까?
리텐션 기간이 늘어나면 디스크 사용량이 공식과 어긋나는 가장 흔한 원인은 카디널리티(cardinality, 레이블 조합으로 만들어지는 고유 시계열의 개수) 증가예요. 공식 needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample은 samples_per_second가 고정값이라고 가정하지만, 실제 운영 환경에서는 컨테이너를 새로 띄우거나 서비스를 늘릴 때마다 레이블 조합이 늘어나면서 시계열 개수 자체가 계속 증가해요. 특히 컨테이너 ID나 요청 경로처럼 값이 자주 바뀌는 레이블을 지표에 붙이면 시계열이 기하급수적으로 늘어날 수 있는데, 이렇게 되면 압축률과 무관하게 저장해야 할 시계열 자체가 많아져서 계산했던 값보다 디스크가 훨씬 빨리 차게 돼요.
그래서 리텐션을 길게 잡을수록 retention.time만 믿기보다 retention.size 옵션을 함께 걸어 상한선을 만들어두는 게 안전해요. 공식 문서도 사이즈 기반 리텐션을 쓸 때는 실제 디스크 용량의 80~85% 수준으로 여유를 두라고 권장하는데, WAL(Write-Ahead Log) 파일과 압축 중인 블록이 추가로 공간을 차지하기 때문이에요.
계산값이 맞는지 실제 환경에서 어떻게 검증할까?
계산으로 나온 예상치가 실제와 맞는지는 Prometheus가 스스로 노출하는 지표로 검증할 수 있어요. rate(prometheus_tsdb_compaction_chunk_size_bytes_sum[2h]) / rate(prometheus_tsdb_compaction_chunk_samples_sum[2h])라는 쿼리를 Grafana 대시보드에 올려두면, 지금 내 환경에서 샘플 하나가 실제로 몇 바이트로 압축되고 있는지 실시간으로 확인할 수 있어요. 이 값이 Gorilla 논문의 1.37바이트나 공식 문서의 1~2바이트 범위와 크게 벗어난다면 카디널리티 문제를 의심해볼 신호가 돼요.
디스크 자체의 여유 공간은 Netdata로 초 단위 사용률을 확인하거나, Prometheus 데이터를 아예 TrueNAS Scale 같은 NAS 스토리지 위의 볼륨에 올려서 ZFS의 여유 공간 관리 기능과 함께 운영하는 방법도 있어요. 계산은 계획을 세우는 도구일 뿐이고, 실측값으로 계속 확인하는 습관이 디스크 부족 사고를 막아주는 진짜 안전장치예요.
결론
Gorilla 논문이 밝힌 16바이트에서 1.37바이트로의 압축은 단순한 학술적 성과가 아니라, 지금 이 순간 홈랩의 Prometheus 컨테이너 안에서 실제로 작동하고 있는 기술이에요. 공식 문서의 needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample 공식에 이 압축률을 대입해보면, 리텐션을 30일로 할지 1년으로 할지가 홈랩 디스크에 실제로 얼마나 부담을 주는지 숫자로 확인할 수 있어요. 다만 이 공식은 시계열 개수가 안정적일 때만 정확하다는 점을 기억하고, 레이블 설계로 카디널리티를 관리하면서 prometheus_tsdb_compaction_chunk_size_bytes_sum 같은 실측 지표로 계산값을 주기적으로 검증하는 것까지가 리텐션 계획의 완성이에요.
출처
- Tuomas Pelkonen, Scott Franklin, Justin Teller, Paul Cavallaro, Qi Huang, Justin Meza, Kaushik Veeraraghavan, “Gorilla: A Fast, Scalable, In-Memory Time Series Database,” Proceedings of the VLDB Endowment, Vol. 8, No. 12 (VLDB 2015), Facebook Inc. — 원문 PDF
- Prometheus 공식 문서, “Storage” — prometheus.io/docs/prometheus/latest/storage
자주 묻는 질문
Q1. Prometheus는 샘플 하나를 저장하는 데 실제로 몇 바이트를 쓰나요?
Prometheus 공식 문서는 평균 1~2바이트라고 밝히고 있고, 이 수치는 XOR 인코딩과 delta-of-delta 타임스탬프 압축을 조합한 결과로 Gorilla 논문이 보고한 1.37바이트와 같은 계열의 압축 기법에서 나온 값이에요.
Q2. Gorilla 논문의 압축률이 Prometheus에도 그대로 적용되나요?
완전히 동일하지는 않지만 같은 계열이에요. Prometheus TSDB는 Gorilla 논문의 delta-of-delta 타임스탬프 압축과 XOR 값 압축 아이디어를 그대로 채택해 구현했기 때문에, 실측 바이트/샘플 값이 Gorilla가 보고한 1.37바이트와 비슷한 1~2바이트 범위에 들어와요.
Q3. 15초 스크레이프 간격으로 1년치 데이터를 보관하려면 디스크가 얼마나 필요한가요?
시계열 개수에 따라 다르지만, 예를 들어 호스트 3대에서 시계열 2,400개를 15초마다 수집한다면 초당 160샘플이 쌓이고, 1년(31,536,000초) 기준으로는 약 6.4GiB(1.37바이트/샘플)에서 9.4GiB(2바이트/샘플) 사이의 디스크가 필요해요.
Q4. 레이블 카디널리티가 늘어나면 용량 계산 공식이 왜 안 맞게 되나요?
공식은 시계열 개수가 고정돼 있다는 가정 위에서 작동하는데, 컨테이너 ID나 사용자 ID처럼 값이 계속 바뀌는 레이블을 붙이면 시계열 개수 자체가 폭증해 samples_per_second 항목이 예상보다 훨씬 커지기 때문이에요.
Q5. retention.time과 retention.size 중 어떤 걸 기준으로 잡아야 하나요?
디스크 용량이 넉넉하지 않은 홈랩이라면 retention.size를 함께 설정하는 게 안전해요. retention.time만 믿고 있으면 예상보다 시계열이 늘어났을 때 디스크가 가득 차버릴 수 있지만, retention.size를 상한으로 걸어두면 그 이상은 오래된 블록부터 자동으로 삭제되기 때문이에요.
Q6. 압축률을 실제로 확인하려면 어떻게 하나요?
Prometheus가 자체 노출하는 지표인 prometheus_tsdb_compaction_chunk_size_bytes_sum과 prometheus_tsdb_compaction_chunk_samples_sum의 비율을 계산하면 내 환경에서 실제로 압축되고 있는 바이트/샘플 값을 직접 확인할 수 있어요.
관련 글


