hot 데이터 이관은 시간 기준이면 파티션 DROP, 개수 기준이면 capped eviction이다
·수정 2회
요약
- hot→cold 이관 기준이 시간이냐 유저별 개수냐에 따라 메커니즘이 완전히 달라진다.
- 시간 기준은 전역 컷라인이라 파티션 통째
DROP(벌크·쌈)으로 끝나지만, 개수 기준은 컷라인이 유저마다 달라 벌크가 안 통하고 유저별로 잘라내야 한다. - 개수 기준은 write 시점 capped eviction(ring buffer)으로 풀고, hot 크기 상한이 보장되며 페이징 경계가 깔끔해진다.
본문
"최근 데이터는 자주, 옛날 데이터는 가끔" 접근 패턴에서 hot 저장소를 작게 유지하려면 오래된 데이터를 cold(S3/BigQuery 등)로 내려야 한다. 이때 "무엇을 오래됐다고 볼 것인가" 의 기준이 두 가지고, 둘은 운영 메커니즘이 정반대다.
시간 기준 (time-based)
created_at 으로 파티셔닝하고, 만료된 파티션을 통째로 cold로 보낸다.
- 전역 컷라인: "2024-03 파티션" 은 모든 유저에게 동시 적용된다.
- 그래서 벌크 연산이 통한다 —
DELETE수십억 건이 아니라DROP PARTITION한 방. 메타데이터 락만 잡고 즉시(거의)다 — row를 하나씩 지우지 않고 파일/세그먼트를 떨궈낸다. (S3 쪽은 date prefix로 스토리지 클래스 전환/삭제를 lifecycle로 자동화.) - 단점: hot에 남는 양이 유저마다 들쭉날쭉. 헤비 유저는 최근 파티션에도 수천 건이 쌓여 hot 크기 상한이 없다.
- 페이징 경계(최근 N개월의 N번째 아이템)가 유저마다 다른 인덱스에 걸린다.
개수 기준 (count-based / 유저당 고정 N개)
"유저당 최근 N개(=고정 페이지 수)만 hot" 정책.
- 컷라인이 유저마다 다르다: 헤비 유저의 N번째는 2달 전, 라이트 유저의 N번째는 3년 전. → 깔끔한 시간 파티션을 못 떨어내므로
DROP PARTITION류 벌크가 안 통한다. 유저별로 잘라야 한다. - 해법은 write 시점 capped eviction (ring buffer): 저장할 때마다 카운트가 N을 넘으면 가장 오래된 1건을 밀어내 cold-write 큐(Kafka/SQS→S3)로 흘려보낸다. 배치 스윕으로 전체 유저를 훑지 않고, 넘친 유저만 넘친 순간 1건 처리.
- hot 크기가
유저 수 × N으로 상한 보장 — 시간 기준의 "무한정 증가" 문제가 사라진다. - Redis sorted set이 이 패턴에 딱 맞는다 (
score=created_at):
ZADD user:{id}:saved {created_at} {post_id}
ZREMRANGEBYRANK user:{id}:saved 0 -(N+1) # N개 초과분 트림 → cold로
두 명령은 각각만 원자적이고 별개 호출이라 그룹 원자성은 없다. 사이에 다른 쓰기가 끼어들 수 있으므로, "트림이 원자적이어야 한다"면
MULTI/EXEC나 Lua로 묶는다 (보통 eviction 1건이 한 틱 늦는 정도라 실무상 무시 가능).
- 페이징은 오히려 더 깔끔하다. N을 page_size 배수로 잡으면 hot/cold 경계가 페이지 경계에 정렬돼, "한 페이지가 hot 18 + cold 2로 걸침" 문제가 사라진다. page 1~K는 hot, K+1부터 cold.
트레이드오프 요약
| 시간 기준 | 개수 기준 | |
|---|---|---|
| 이관 | 파티션 통째 DROP (벌크·쌈) | 유저별 eviction (write 시점) |
| hot 크기 | 무한정 ↑ 가능 | 유저당 N으로 상한 보장 |
| 운영 | 단순 (전역 컷) | 유저별 처리 |
| 페이징 경계 | 유저마다 들쭉날쭉 | 페이지 정렬·고정 |
둘을 섞어 "최근 N개 또는 최근 M개월, 먼저 걸리는 쪽" 으로 갈 수도 있으나, 운영 단순성을 위해 보통 한쪽만 명확히 정한다.
관련 노트
- 파티셔닝은 논리적 한 테이블을 독립된 물리 세그먼트로 쪼개 안 읽을 조각을 통째로 건너뛴다
- S3에 date prefix를 붙이면 스토리지 클래스 전환 및 삭제를 자동화 할 수 있다.
- Redis cluster는 단일 노드의 메모리 용량, 쓰기 처리량 한계를 수평 확장으로 해결한다.