이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 저장량, 대역폭, 메모리를 어떤 입력값으로 계산하는가?
- payload 크기보다 index, 복제, 보존 기간이 더 중요한 경우는 언제인가?
- storage 추정이 DB 선택, cache 정책, 보관 정책으로 어떻게 이어지는가?
개요
트래픽 추정은 CPU와 QPS만 계산하는 일이 아니다.
서비스가 오래 운영될수록 저장량, 네트워크 대역폭, cache memory가 비용과 장애의 중심이 된다.
특히 이벤트, 알림, 로그, 검색 색인, 피드 read model은 요청 하나가 여러 저장소에 흔적을 남긴다.
이벤트 1KB가 초당 1,000건이면 하루 86GB라는 계산은 출발점일 뿐이다.
실제 운영에서는 index, replication, compression, retention, backup, object storage 이동, 분석 적재까지 포함해야 한다.
이 문서가 바꾸는 설계 축
| 축 | 바뀌는 내용 |
|---|---|
| 성능 | payload 크기와 hot set을 보고 DB, cache, object storage의 역할을 나눈다. |
| 정합성 | 원본 데이터와 파생 데이터의 보존 기간, 재생성 가능 여부를 구분한다. |
| 장애 격리 | 저장소가 가득 차거나 bandwidth가 막힐 때 핵심 요청과 배치 요청을 분리한다. |
| 운영 복잡도 | retention, 삭제, 압축, 재처리, 비용 모니터링 절차가 생긴다. |
계산 입력값
저장량 계산은 다음 입력을 요구한다.
| 입력 | 질문 |
|---|---|
| event count | 하루에 몇 건이 쌓이는가? |
| payload size | 한 건의 평균 크기와 p95 크기는 얼마인가? |
| index overhead | 조회 조건 때문에 몇 개의 index가 필요한가? |
| retention | 원본을 며칠 보관해야 하는가? |
| replication | 장애 대비로 몇 벌 저장되는가? |
| compression | 압축률을 기대할 수 있는 데이터인가? |
| hot set | cache에 올려야 하는 최근 데이터는 어느 범위인가? |
| egress | 응답으로 밖으로 나가는 데이터는 얼마인가? |
| 평균 payload만 쓰면 위험하다. | |
| 이미지 URL 목록, metadata, JSON 확장 필드는 p95 크기를 크게 만든다. | |
| DB row가 작아도 index가 많으면 write 비용과 저장량이 같이 늘어난다. |
대표 시나리오
알림 발송 시스템을 예로 든다. 사용자 행동 이벤트를 저장하고, 일부 이벤트는 알림 발송과 집계에 쓰인다.
| 항목 | 값 |
|---|---|
| 일 이벤트 수 | 100,000,000건 |
| 원본 payload 평균 | 2KB |
| payload p95 | 6KB |
| 원본 보존 기간 | 30일 |
| 집계 보존 기간 | 365일 |
| index overhead | 원본 크기의 1.2배 |
| replication factor | 3 |
| 압축률 | 50% |
| 이 시나리오에서 원본 event store와 집계 테이블을 같은 방식으로 보관하면 비용이 빠르게 커진다. | |
| 원본은 재처리와 감사에 필요하고, 집계는 조회와 리포트에 필요하다. | |
| 둘은 retention과 query pattern이 다르다. |
원본 저장량 계산
먼저 압축 전 논리 저장량을 계산한다.
daily_raw = 100,000,000 * 2KB
= 200,000,000KB
= 약 190GB/day
raw_30_days = 190GB * 30
= 5.7TBindex overhead를 더한다.
raw_with_index = 5.7TB * (1 + 1.2)
= 12.54TBreplication factor와 compression을 반영한다.
physical_storage = 12.54TB * 3 * 0.5
= 18.81TB처음 계산한 5.7TB와 실제 물리 저장량 18.81TB는 차이가 크다. 이 차이를 보지 않으면 디스크 비용, backup 시간, 장애 복구 시간을 과소평가한다.
p95 payload의 의미
평균 2KB만 보면 안전해 보인다. 하지만 p95가 6KB라면 피크 시간의 network와 broker memory는 더 크게 잡아야 한다.
peak_event_tps = 8,000
p95_payload = 6KB
ingress_bandwidth = 8,000 * 6KB
= 48,000KB/sec
= 약 384MbpsTLS, HTTP header, broker protocol overhead까지 포함하면 더 커진다. 평균 payload는 저장 비용 추정에 쓰고, p95 payload는 피크 bandwidth와 buffer memory 추정에 쓴다.
대역폭 계산
대역폭은 inbound와 outbound를 나눠야 한다. 알림 목록 API가 사용자당 20개 알림을 내려준다고 하자.
notification_item_size = 900B
items_per_response = 20
response_size = 18KB
peak_read_qps = 5,000
outbound = 5,000 * 18KB
= 90,000KB/sec
= 약 720Mbps응답 압축이 60%로 줄여도 약 288Mbps다. 이 값은 API 서버 network뿐 아니라 CDN, gateway, egress 비용과 연결된다. 사용자가 모바일 환경이면 큰 응답은 latency와 배터리 비용도 만든다.
cache memory 계산
cache memory는 전체 데이터가 아니라 hot set으로 계산한다. 예를 들어 최근 24시간 알림 목록만 cache한다고 하자.
daily_active_users = 1,000,000
cached_users = 200,000
avg_cached_list_size = 20KB
redis_overhead_factor = 1.6
memory = 200,000 * 20KB * 1.6
= 6.4GB여기에 replication과 fragmentation을 더하면 실제 클러스터는 더 필요하다. cache에 전체 데이터를 올리는 선택은 간단하지만 비용이 크다. TTL을 짧게 잡으면 memory는 줄지만 miss와 DB read가 늘어난다.
DB와 object storage를 나누는 기준
모든 데이터를 관계형 DB에 오래 두면 조회는 편하지만 write, vacuum, backup, migration 비용이 커진다. 반대로 너무 빨리 object storage로 옮기면 재처리와 검색이 어려워진다.
| 데이터 | 주 저장 위치 | 이유 |
|---|---|---|
| 최근 알림 상태 | RDB 또는 key-value store | 사용자 화면과 read-after-write가 중요하다. |
| 원본 이벤트 30일 | event store 또는 log storage | 재처리와 장애 분석에 필요하다. |
| 오래된 원본 | object storage | 저비용 보관과 batch 재처리에 적합하다. |
| 일별 집계 | OLAP 또는 summary table | 빠른 리포트 조회가 목적이다. |
핵심은 자주 읽는 데이터, 정확히 보존해야 하는 데이터, 재생성 가능한 데이터를 분리하는 것이다. | ||
| 이 분리가 없으면 성능 문제를 저장소 증설로만 해결하게 된다. |
schema와 index 판단
저장량 추정은 schema 설계와 연결된다.
CREATE TABLE notification_event (
event_id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
event_type VARCHAR(40) NOT NULL,
occurred_at TIMESTAMP NOT NULL,
payload JSONB NOT NULL
);
CREATE INDEX idx_notification_event_user_time
ON notification_event (user_id, occurred_at DESC);이 index는 사용자별 최근 이벤트 조회에는 좋다. 하지만 event_type, campaign_id, device_id마다 index를 추가하면 저장량과 write latency가 늘어난다. 조회 요구사항이 확정되지 않은 필드까지 모두 index로 만들면 capacity planning이 실패한다.
운영 지표
저장량 추정은 다음 지표로 검증한다.
| 지표 | 보는 이유 |
|---|---|
| bytes written per second | write bandwidth와 storage 증가 속도를 본다. |
| table size by day | retention 정책이 실제로 작동하는지 본다. |
| index size ratio | index가 data보다 커지는지 확인한다. |
| cache memory used | hot set 계산이 맞는지 본다. |
| cache hit ratio | TTL과 memory 사이의 균형을 본다. |
| egress bytes | 응답 크기와 비용을 본다. |
| disk fill forecast | 며칠 뒤 용량 한계에 닿는지 예측한다. |
| 디스크 사용률 90% alert만으로는 늦다. | |
| 증가 속도와 남은 일수를 같이 봐야 한다. |
위험 신호
- payload 평균만 보고 p95 크기와 header overhead를 보지 않는다.
- index와 replication을 저장량 계산에서 제외한다.
- 원본 데이터와 집계 데이터의 보존 기간을 똑같이 둔다.
- cache memory를 전체 row 수로만 계산하고 hot set을 정의하지 않는다.
- 응답 크기와 egress 비용을 API 설계에서 다루지 않는다.
- 디스크가 가득 찬 뒤에야 retention 정책을 논의한다.
- 저장소 장애가 핵심 API와 batch API에 같은 영향을 주는지 확인하지 않는다.
확인 질문
확인 질문
- 확인 질문: 저장량 계산에서 원본 payload만 보면 왜 부족한가?
- 답변: index, replication, compression, 보존 기간, backup, 파생 데이터가 실제 용량과 복구 시간을 크게 바꾸기 때문이다.
- 확인 질문: cache memory는 전체 데이터로 계산해야 하는가?
- 답변: 아니다. 실제로 자주 읽는 hot set, TTL, value 크기, Redis overhead로 계산해야 한다.
- 확인 질문: 이 문서의 핵심 trade-off는 무엇인가?
- 답변: 데이터를 오래, 빠르게, 많이 보관할수록 조회와 재처리는 쉬워지지만 비용과 운영 복잡도가 커진다는 점이다.