DB의 테이블의 row가 너무 많아질떄 (예 하루에 10만건씩 누적), 대처 방법
- “하루 10만 건씩 계속 누적”이면 1년에 3,650만 건입니다. 초반엔 버티지만, 몇 달만 지나도 쿼리·백업·VACUUM/OPTIMIZE 비용이 급증하죠. 아래 순서대로 적용해보세요.
1) 먼저 결정할 것 (운영 정책)
- 보존 기간: 예) “6개월은 온라인(핫), 2년은 아카이브(콜드), 그 외 삭제”
- 주요 조회 패턴: 최근 N일만 조회? 특정 사용자/키 기준 조회? 집계 위주?
- SLA/성능: p95 응답시간, 허용 가능한 쓰기 지연
2) 즉시 효과 (스키마/인덱스/쓰기)
-
인덱스 슬림화: 실제 조건/정렬에 쓰는 컬럼만 인덱스. 불필요한 복합 인덱스 제거.
-
Covering/Partial 인덱스:
-
PostgreSQL: 최근 데이터만 대상으로 부분 인덱스
CREATE INDEX CONCURRENTLY idx_events_recent ON events(user_id, created_at DESC) WHERE created_at >= now() - interval '30 days'; -
대용량 타임순 데이터는 BRIN 인덱스 고려(append-only에 매우 저렴).
CREATE INDEX CONCURRENTLY idx_events_brin ON events USING BRIN(created_at);
-
-
배치 쓰기: 단건 insert 대신 배치(예: 500~5,000건 단위), 트랜잭션 묶기.
-
PK 설계: InnoDB/Clustered PK는 증가형(빅시리얼/스노우플레이크). 랜덤 UUID v4를 클러스터 키로 쓰지 않기.
3) 파티셔닝 (핵심)
대부분의 “시간 기반 로그/이벤트”는 기간별 파티션으로 해결됩니다. 파티션 단위는 월(일간 트래픽이 높으면 “주/일”).
-
장점:
DROP PARTITION으로 오래된 데이터 제거가 O(메타데이터)급. 유지보수·백업·인덱스가 파티션별로 슬림. -
PostgreSQL 예시 (월 파티션):
CREATE TABLE events ( id bigserial PRIMARY KEY, user_id bigint NOT NULL, created_at timestamptz NOT NULL, payload jsonb ) PARTITION BY RANGE (created_at); CREATE TABLE events_2025_10 PARTITION OF events FOR VALUES FROM ('2025-10-01') TO ('2025-11-01'); -- 앞으로 매월 자동 생성(배치 잡) + 오래된 파티션 DROP DROP TABLE IF EXISTS events_2024_10; -- 보존기간 지난 달은 즉시 삭제 -
MySQL/InnoDB: RANGE 파티션(TO_DAYS(created_at)) 또는 HASH(user_id). 오래된 파티션
ALTER TABLE ... DROP PARTITION.
4) 보존/아카이브/요약(롤업)
-
TTL/만료 정책: 파티션 드롭으로 실행(DELETE보다 훨씬 빠름).
-
아카이브 테이블/스토리지:
-
OLTP에는 최근 3~6개월만 두고, 이전은 아카이브 스키마로 이동(또는 S3/Coldw storage + Athena/BigQuery/ClickHouse).
-
예:
INSERT INTO events_archive SELECT * FROM events WHERE created_at < now() - interval '180 days'; -- 그 후 해당 파티션 DROP
-
-
롤업 테이블/머터리얼라이즈드 뷰: 조회가 “일/주 단위 집계”라면 사전 집계.
CREATE MATERIALIZED VIEW daily_counts AS SELECT date_trunc('day', created_at) AS day, count(*) AS cnt FROM events GROUP BY 1; -- 주기적 갱신 REFRESH MATERIALIZED VIEW CONCURRENTLY daily_counts;
5) 운영/튜닝
- Autovacuum/Analyze(PostgreSQL): 대용량 테이블은 파라미터 하향 조정(빠른 회전), 파티션별 병렬 VACUUM.
예)
autovacuum_vacuum_scale_factor0.1→0.02,autovacuum_analyze_scale_factor0.05 등. - 백업 전략: 파티션 단위 백업. 오래된 파티션은 스냅샷만 보관.
- 모니터링: 파티션 개수, 인덱스 크기, 힙블로트, 느린 쿼리 패턴 추적.
6) 필요 시 아키텍처 분리
- 쓰기 많은 로그/이벤트: TimescaleDB(PSQL 확장), ClickHouse, BigQuery 같은 시계열/컬럼 지향으로 분리. OLTP(트랜잭션)는 RDB, 분석/검색은 OLAP/서치로 CQRS/ETL.
- CQRS 파이프라인: OLTP → (Debezium/Kafka CDC) → 스트리밍 집계/보관.
7) 현실적인 적용 조합(권장 기본셋)
- “기간 기준 파티셔닝(월)” + “최근 구간 Partial/BRIN 인덱스”
- “6개월 TTL(파티션 드롭)” + “일간 롤업 뷰”
- “아카이브는 S3/저비용 스토리지(압축/파케이)”
- “배치 insert + PK 증가형” → 대부분의 10만건/일 워크로드는 이 4가지로 깔끔히 안정화됩니다.