[Daily morning study] Time-series 데이터베이스 개념과 활용
#daily morning study
Time-series 데이터베이스란
Time-series 데이터베이스(TSDB)는 시간 순서로 기록된 데이터를 저장하고 조회하는 데 최적화된 데이터베이스다. 일반 RDB와 달리 타임스탬프를 기본 인덱스로 사용하고, 시간 범위 쿼리와 집계 연산에 특화된 저장 구조를 가진다.
시계열 데이터의 특성
- 추가 위주(append-only): 새로운 데이터가 계속 들어오지만 과거 데이터를 수정하는 경우는 드물다.
- 높은 쓰기 처리량: 수천 개의 센서나 서버 메트릭이 1초 단위로 데이터를 발생시킨다.
- 시간 기반 범위 조회: “최근 1시간”, “어제 오후 3시~5시” 같은 시간 범위 쿼리가 대부분이다.
- 자동 만료(retention): 오래된 데이터는 용량 절약을 위해 자동으로 삭제하거나 압축한다.
대표적인 활용 사례
- 서버 메트릭(CPU, 메모리, 네트워크) 모니터링
- IoT 센서 데이터 수집
- 주식·코인 가격 이력
- 사용자 행동 이벤트 로그
RDB와의 비교
일반 RDB에 시계열 데이터를 저장하면 아래 문제가 생긴다.
-- 서버 메트릭 테이블 예시 (RDB)
CREATE TABLE metrics (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
server_id VARCHAR(50),
metric VARCHAR(50),
value DOUBLE,
ts DATETIME
);
- 행(row) 하나에 메타데이터(server_id, metric 이름)가 중복 저장되어 공간 낭비가 심하다.
- 시간 범위 조회를 위한 인덱스를 별도로 관리해야 한다.
- 초당 수십만 건의 INSERT를 처리하기 어렵다.
TSDB는 이 문제를 다음과 같이 해결한다.
| 항목 | RDB | TSDB |
|---|---|---|
| 기본 인덱스 | PK (id) | 타임스탬프 |
| 쓰기 최적화 | 트랜잭션 우선 | 배치 압축 쓰기 |
| 과거 데이터 처리 | 수동 파티셔닝 | 자동 downsampling/retention |
| 공간 효율 | 낮음 (메타데이터 중복) | 높음 (델타 인코딩, 압축) |
주요 TSDB 제품
InfluxDB
가장 널리 쓰이는 오픈소스 TSDB. 자체 쿼리 언어인 Flux(또는 InfluxQL)를 사용한다.
# InfluxDB 3.x 기준 (line protocol로 데이터 쓰기)
# measurement,tag_key=tag_value field_key=field_value timestamp
cpu,host=server01,region=us-east usage_idle=87.3,usage_user=12.7 1728432000000000000
- Measurement: 관계형 DB의 테이블에 해당
- Tag: 인덱싱되는 메타데이터 (서버명, 지역 등)
- Field: 실제 측정값 (CPU 사용률, 온도 등)
- Timestamp: 나노초 단위까지 저장 가능
from influxdb_client import InfluxDBClient, Point
from datetime import datetime
client = InfluxDBClient(url="http://localhost:8086", token="my-token", org="my-org")
write_api = client.write_api()
point = (
Point("cpu_usage")
.tag("host", "server01")
.field("usage_idle", 87.3)
.field("usage_user", 12.7)
.time(datetime.utcnow())
)
write_api.write(bucket="monitoring", record=point)
Prometheus
쿠버네티스 환경에서 표준처럼 쓰이는 풀(pull) 기반 모니터링 TSDB. 자체 쿼리 언어 PromQL을 사용한다.
# 지난 5분간 평균 CPU 사용률
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 메모리 사용량이 80% 초과인 인스턴스
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.8
Prometheus는 데이터를 직접 저장하기보다 단기 보관용으로 쓰고, 장기 보관은 Thanos나 VictoriaMetrics 같은 외부 저장소에 위임하는 경우가 많다.
TimescaleDB
PostgreSQL의 확장 기능으로 동작하는 TSDB. 기존 SQL 문법을 그대로 쓸 수 있어 RDB 경험이 있는 팀이 도입하기 쉽다.
-- TimescaleDB 하이퍼테이블 생성
CREATE TABLE metrics (
ts TIMESTAMPTZ NOT NULL,
host TEXT,
cpu_idle DOUBLE PRECISION,
cpu_user DOUBLE PRECISION
);
SELECT create_hypertable('metrics', 'ts');
-- 일반 SQL로 시간 범위 조회
SELECT time_bucket('1 minute', ts) AS bucket,
host,
AVG(cpu_idle) AS avg_idle
FROM metrics
WHERE ts > NOW() - INTERVAL '1 hour'
GROUP BY bucket, host
ORDER BY bucket DESC;
저장 구조 — 왜 빠른가
시간 기반 청크(Chunk) 분할
TSDB는 데이터를 시간 범위별로 청크(파티션)로 나눠 저장한다.
[2026-10-09 00:00 ~ 01:00] → chunk_001
[2026-10-09 01:00 ~ 02:00] → chunk_002
...
[2026-10-09 23:00 ~ 00:00] → chunk_024
최근 1시간을 조회할 때 전체 테이블을 스캔하지 않고 해당 청크만 읽는다.
델타 인코딩과 압축
시계열 데이터는 연속적인 값의 변화량(delta)이 작은 경우가 많다. Gorilla 압축 알고리즘(InfluxDB, Prometheus 채택)은 다음 원리를 사용한다.
원래 값: 87.3, 87.5, 87.4, 88.0, 87.9
첫 번째: 87.3 (전체 저장)
이후: +0.2, -0.1, +0.6, -0.1 (차이만 저장, 비트 압축)
이 방식으로 일반 부동소수점 저장 대비 1/10 수준의 공간으로 줄이는 경우도 있다.
Retention Policy와 Downsampling
Retention Policy
오래된 데이터를 자동 삭제하는 규칙이다.
-- InfluxDB: 30일 지난 데이터 자동 삭제
CREATE RETENTION POLICY "30days" ON "monitoring"
DURATION 30d REPLICATION 1 DEFAULT;
Downsampling (연속 쿼리)
원시 데이터는 단기 보관하고, 집계된 데이터는 장기 보관하는 전략이다.
원시 데이터 (1초 단위) → 7일 보관
1분 평균 집계 → 30일 보관
1시간 평균 집계 → 1년 보관
# InfluxDB Flux로 downsampling 예시
from(bucket: "raw_metrics")
|> range(start: -1h)
|> filter(fn: (r) => r._measurement == "cpu_usage")
|> aggregateWindow(every: 1m, fn: mean, createEmpty: false)
|> to(bucket: "downsampled_metrics")
실제 스택 구성 예시
서버 모니터링 (Prometheus + Grafana)
서버/컨테이너
↓ (메트릭 노출, /metrics 엔드포인트)
Node Exporter / cAdvisor
↓ (scrape, 15초마다 pull)
Prometheus
↓ (PromQL 쿼리)
Grafana (대시보드 시각화)
IoT 데이터 수집 (InfluxDB)
IoT 디바이스
↓ (MQTT 프로토콜)
Telegraf (데이터 수집 에이전트)
↓ (Line Protocol)
InfluxDB
↓ (Flux/InfluxQL)
Grafana
Telegraf는 InfluxDB에서 만든 오픈소스 에이전트로, 200개 이상의 플러그인으로 다양한 소스(MySQL, Redis, Kafka 등)에서 메트릭을 수집해 TSDB에 쓸 수 있다.
카디널리티(Cardinality) 문제
TSDB에서 흔히 발생하는 성능 이슈다. InfluxDB와 Prometheus는 태그(레이블) 조합 수를 기준으로 인덱스를 관리한다.
host=server01, region=us-east, env=prod → 1개 시리즈
host=server02, region=us-east, env=prod → 1개 시리즈
...
태그 값의 종류가 많아지면(예: user_id를 태그로 사용) 시리즈 수가 폭발적으로 증가해 메모리 사용량과 인덱스 크기가 급증한다. 이를 high cardinality 문제라고 부른다.
해결 방법
- user_id처럼 고유값이 많은 항목은 태그 대신 field로 저장한다.
- 불필요하게 세분화된 태그는 제거한다.
- Prometheus의 경우 레이블 수를 최소화하고, 레코딩 룰(recording rules)로 사전 집계한다.
정리
| 구분 | InfluxDB | Prometheus | TimescaleDB |
|---|---|---|---|
| 주요 용도 | IoT, 일반 메트릭 | 쿠버네티스 모니터링 | PostgreSQL 기반 확장 |
| 쿼리 언어 | Flux / InfluxQL | PromQL | SQL |
| 수집 방식 | Push | Pull | Push |
| 장기 보관 | 자체 지원 | 외부 저장소 필요 | 자체 지원 |
| 학습 곡선 | 중간 | 중간 | 낮음 (SQL 재사용) |
시계열 데이터는 발생 빈도와 양이 많고 시간 범위 조회가 핵심이라, RDB 대신 TSDB를 사용하면 저장 공간과 쿼리 성능 모두에서 큰 이점을 얻을 수 있다. 모니터링 스택을 구성할 때 Prometheus+Grafana 조합을 기본으로 익혀두면 실무에 바로 활용할 수 있다.