retention과 compaction — 메시지가 언제, 어떻게 사라지는가
Kafka는 메시지를 읽어도 지우지 않는다(장 01·02). 그럼 디스크는 계속 찰 수밖에 없나? 아니다 — topic마다 "언제, 무엇을 지울지" 정책(retention/compaction)을 둬서 오래된 데이터를 정리한다. 이 글은 그 두 가지 규칙과 실제 설정 방법을 다룬다.
주의할 점부터: "compaction"은 압축(zip)이 아니다. 같은 key의 옛 메시지를 버리는 것이다. 이 용어 혼동이 운영 착오를 자주 만든다.
메시지는 영원히 남지 않는다 — 왜 retention이 필요한가
Kafka topic은 로그라 메시지가 계속 쌓인다. 하지만 디스크는 유한하다. 그래서 topic마다 정리 정책을 둔다. 두 가지:
- delete — 오래된 것을 시간이나 크기 기준으로 버린다.
- compact — 같은 key의 옛 값을 버리고 최신값만 남긴다.
이 둘은 "언제 지울지"의 차이가 아니라 "무엇을 지울지"의 차이다.
segment — 로그를 이루는 파일 단위
partition 하나는 여러 segment로 구성된다 (log.md). 각 segment는 세 파일로 이루어진다:
| 파일 | 역할 |
|---|---|
.log |
실제 메시지 데이터(순차 append) |
.index |
offset → 파일 내 바이트 위치 매핑(빠른 임의 접근) |
.timeindex |
timestamp → offset 매핑(시간 기반 조회·retention) |
segment는 log.segment.bytes(기본 1 GiB)에 도달하거나 log.segment.ms(시간)가 지나면 새 segment로 넘어간다(rolling). active segment(마지막, 쓰기 진행 중)는 삭제 대상이 아니다 — rolling되어 읽기 전용이 된 segment만 삭제 후보.
segment가 왜 중요한가
"consumer가 offset 5000을 읽고 싶다"고 할 때, partition 전체를 처음부터 훑으면 느리다. .index 파일이 offset 5000이 어느 바이트 위치에 있는지 알려주어 O(1)에 찾는다. retention도 segment 단위로 일어나서 — 파일 중간을 수정할 필요 없이 segment 전체를 삭제(append-only 유지).
delete 정책 — 오래된 segment를 통째로 버린다
cleanup.policy=delete(기본)는 오래된 segment를 통째로 버린다. 두 기준:
- 시간 기반: segment 내 가장 큰 timestamp가
retention.ms(기본 7일 = 604800000ms)를 넘으면 그 segment 삭제. - 크기 기반: partition 크기가
retention.bytes(기본 -1 = 비활성) 한계를 넘으면 가장 오래된 segment부터 삭제.
둘 다 켜면 어떤 쪽이든 임계에 닿으면 삭제(먼저 닿는 쪽).
retention 설정 예
topic 생성 시 retention을 1시간으로 설정:
# Kafka 4.3
bin/kafka-topics.sh --create --topic short-lived \
--partitions 3 --replication-factor 1 \
--config retention.ms=3600000 \
--bootstrap-server localhost:9092
topic별로 동적 변경:
bin/kafka-configs.sh --alter --topic short-lived \
--add-config retention.ms=7200000,retention.bytes=1073741824 \
--bootstrap-server localhost:9092
현재 설정 확인:
bin/kafka-configs.sh --describe --topic short-lived --bootstrap-server localhost:9092
retention 시나리오 — 시간순 타임라인
시각 0: partition 생성, segment-0 활성화
시각 1h: segment-0이 1GiB 도달 → segment-1로 rolling
시각 7d: segment-0의 max timestamp가 7일(retention.ms) 초과 → segment-0 삭제
시각 8d: segment-1 삭제...이 흐름이 "7일 보관, segment 단위 삭제"가 실제로 어떻게 일어나는지 보여준다.
compact 정책 — 같은 key의 최신값만 남긴다
cleanup.policy=compact는 key 기반이다. 같은 key의 메시지가 여러 개 있으면 가장 최근 것만 남기고 옛 것은 버린다 (design.md — Log Compaction).
예: topic에 user1의 프로필이 v1, v2, v3로 업데이트되며 들어온다. compaction 후엔 user1의 v3만 남는다. 과거(v1, v2)는 버려진다. 이게 "사용자 최신 상태만 유지"가 필요한 topic에 유용한 이유다.
compaction ≠ 압축
"compaction = 압축(zip)"이라고 생각하면 안 된다. 압축은 용량을 줄이지만 데이터는 그대로고, compaction은 옛 데이터 자체를 버린다. 용량은 줄지만 원리가 다르다.
tombstone — "이 key를 삭제하라" 표식
value가 null인 메시지를 tombstone이라 한다. compaction에서 "이 key를 더 이상 유지하지 마라"는 삭제 표식. 일정 기간 후 tombstone 자체도 제거된다.
tombstone 사용 예:
# user1을 삭제하려면 value=null로 produce
bin/kafka-console-producer.sh --topic user-profiles \
--property "parse.key=true" --property "key.separator=:" \
--bootstrap-server localhost:9092
# 입력: user1: (value 없음 = null = tombstone)
dirty ratio — compaction 트리거
min.cleanable.dirty.ratio(기본 0.5) — 한 segment에서 옛 key 값이 일정 비율(dirty) 이상 쌓여야 compact 대상이 됨. 너무 낮으면 빈번 compact(CPU 비용), 너무 높으면 용량 비효율.
key가 없으면 compact는 무의미
key가 null인 메시지는 compact 대상이 아니다 → delete와 혼합(compact,delete) 운영이 권장된다.
delete vs compact — 무엇을 지울 것인가
| 정책 | 무엇이 남나 | 전형 용도 | 설정 |
|---|---|---|---|
delete |
기간 내 전체 이력 | 이벤트 로그, 원시 데이터 | cleanup.policy=delete, retention.ms=604800000 |
compact |
각 key 최신값 | 상태 스냅샷, config, 사용자 프로필 | cleanup.policy=compact, min.cleanable.dirty.ratio=0.5 |
compact,delete |
key 최신 + 기간 제한 | 상태 + tombstone 정리 | cleanup.policy=compact,delete |
Kafka 4.3 실습 — 직접 확인하기
사전: KRaft 단일 노드.
retention 관찰
# Kafka 4.3 — 짧은 retention으로 topic 생성 (1분)
bin/kafka-topics.sh --create --topic ret-demo \
--partitions 1 --replication-factor 1 \
--config retention.ms=60000 \
--bootstrap-server localhost:9092
# 메시지 produce 후 1분 대기 → describe로 segment 확인
bin/kafka-topics.sh --describe --topic ret-demo --bootstrap-server localhost:9092
compaction (key 기반)
# cleanup.policy=compact 로 topic 생성
bin/kafka-topics.sh --create --topic comp-demo \
--partitions 1 --replication-factor 1 \
--config cleanup.policy=compact \
--bootstrap-server localhost:9092
# 같은 key에 여러 값 produce
bin/kafka-console-producer.sh --topic comp-demo \
--property "parse.key=true" --property "key.separator=:" \
--bootstrap-server localhost:9092
# user1:v1 / user1:v2 / user1:v3 입력
확인할 것: compact 실행 후 user1의 최신값(v3)만 남는지(즉시 관찰은 어려움 — dirty ratio·시간 조건 충족 시 실행).
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "compaction = 압축(zip)" | 아님. key별 옛값 제거 |
| "retention은 시간 OR 크기 중 하나만" | 둘 다 켜면 먼저 닿는 쪽 삭제 |
| "key 없이 produce해도 compact된다" | key 없으면 compact 의미 없음 |
| "메시지 단위로 삭제된다" | segment 단위로 통째 삭제 |
| "active segment도 retention 대상" | 아님. rolling 후 후보 |
| "tombstone은 영구 남는다" | 일정 기간 후 제거됨 |
더 깊이
__consumer_offsets는 왜 compact인가: consumer offset은(group,topic,partition)을 key로 저장. 같은 key의 옛 offset은 필요 없음 → compact로 최신만 유지(장 04).- tiered storage (3.x+): 로컬 디스크 외 원격(S3)에 오래된 segment 보관 → 비용 절감.
log.segment.ms: 시간 기반 segment rolling. 기본값은 7일(미검증). 짧게 두면 segment 수가 많아져 파일 핸들 증가.
요약 — 이 글의 결론
- segment: partition을 이루는 파일 단위(
.log+.index+.timeindex). active segment만 쓰기. - delete: 시간(
retention.ms, 기본 7일) 또는 크기(retention.bytes) — 먼저 도달한 쪽. segment 통째 삭제. - compact: key별 최신값만 유지. tombstone(
null)으로 key 삭제. key 없으면 무의미. (압축 아님!) compact,delete: 상태 유지 + 기간 제한 결합.- 설정: topic별로 동적 변경(
kafka-configs.sh --alter --add-config). - delete vs compact는 "무엇을" 지울지의 차이 — 시간/크기(delete) vs key별 옛값(compact).
생각해 볼 문제
- delete와 compact의 차이를 "무엇을 지우는가" 관점에서 설명하라.
- retention 시간과 크기를 둘 다 켜면 어떻게 동작하는가?
- segment를 통째로 삭제하는 이유(append-only)를 설명하라.
- compaction에서 tombstone(value=null)의 역할은? 어떻게 쓰는가?
- key 없이 produce하는데 cleanup.policy=compact인 topic에서 무슨 일이 벌어지는가?
__consumer_offsets가 compact 정책을 쓰는 이유는?- retention.ms를 topic별로 동적 변경하는 명령은?
참고
- Implementation — log.md (segments, deletes) - 접근 2026-07-09
- Design — Log Compaction - 접근 2026-07-09
- Topic configs - 접근 2026-07-09
'Tech Artifacts > Kafka' 카테고리의 다른 글
| Kafka - 09. schema registry (1) | 2026.07.09 |
|---|---|
| Kafka - 08. delivery semantics (0) | 2026.07.09 |
| Kafka - 06. replication (0) | 2026.07.09 |
| Kafka - 05. consumer group (0) | 2026.07.09 |
| Kafka - 04. consumer (0) | 2026.07.09 |
