Data & Platform/Event Streaming

Kafka - 07. log retention

retention과 compaction — 메시지가 언제, 어떻게 사라지는가

운영 6개월차에 디스크가 가득 찼다. Kafka는 메시지를 읽어도 지우지 않는다(장 01·02)고 배웠는데, 그럼 무한정 쌓이는 건가? — 그렇지 않다. topic마다 "언제, 무엇을 지울지" 정책(retention/compaction)을 둬서 오래된 데이터를 정리한다. 하지만 이 정책을 잘못 이해하면, 의도치 않게 중요한 데이터를 날리거나 반대로 디스크를 꽉 채운다.

첫 번째 함정부터 짚고 가자: "compaction"은 압축(zip)이 아니다. 같은 key의 옛 메시지를 버리는 것이다. 이 용어 혼동 하나가 운영 착오를 자주 만든다 — "압축 켰으니 용량 절약되겠지"라고 생각했는데, 사실은 key 기반 데이터 보존 정책을 바꾼 것이다. retention(시간/크기 기준 삭제)과 compaction(key 기반 옛값 제거)이 무엇을 지울지를 어떻게 다르게 결정하는지, segment가 왜 삭제의 최소 단위인지, __consumer_offsets 같은 내부 topic이 왜 compact인지를 추적한다.

메시지는 영원히 남지 않는다 — 왜 retention이 필요한가

Kafka topic은 로그라 메시지가 계속 쌓인다. 하지만 디스크는 유한하다. 그래서 topic마다 정리 정책을 둔다. 두 가지:

  • delete — 오래된 것을 시간이나 크기 기준으로 버린다.
  • compact — 같은 key의 옛 값을 버리고 최신값만 남긴다.

이 둘은 "언제 지울지"의 차이가 아니라 "무엇을 지울지"의 차이다. delete는 시간/크기가 닿은 segment를 통째로 지우고, compact는 key 단위로 옛 값을 지운다. 같은 topic에 둘 다 적용(compact,delete)할 수도 있다 — 예를 들어 "key 최신값 유지하되 30일 이상 된 것은 지운다"는 운영이 가능하다.

cleanup.policy — topic별 정리 정책

retention/compaction은 topic 단위 설정 cleanup.policy로 결정된다:

  • cleanup.policy=delete(기본): 시간/크기 기준 삭제.
  • cleanup.policy=compact: key별 최신값 유지.
  • cleanup.policy=compact,delete: 둘 다.

이 설정은 topic 생성 시 --config로 붙이거나, kafka-configs.sh로 동적 변경할 수 있다. 기본값이 delete이므로, compact가 필요한 topic(상태 저장, 사용자 프로필)은 명시적으로 설정해야 한다.

segment — 로그를 이루는 파일 단위

partition 하나는 여러 segment로 구성된다 (log.md). 각 segment는 세 파일로 이루어진다:

파일 역할
.log 실제 메시지 데이터(순차 append)
.index offset → 파일 내 바이트 위치 매핑(빠른 임의 접근)
.timeindex timestamp → offset 매핑(시간 기반 조회·retention)

segment 파일 이름은 시작 offset이다. 예를 들어 00000000000000000000.log(시작 offset=0), 00000000000000150000.log(시작 offset=150000) 식. 이 이름 규칙 덕에 broker가 segment 목록만 봐도 어느 segment가 어느 offset 범위를 담는지 알 수 있다. partition 디렉토리(log.dirs 아래 topic-partition)에 들어가면 이 파일들이 보인다.

# Kafka 4.3 — partition 디렉토리 내 segment 파일 확인
ls -la /tmp/kafka-logs/comp-demo-0/
00000000000000000000.index
00000000000000000000.log
00000000000000000000.timeindex
00000000000000123456.index  (segment가 rolling됐을 때 추가 파일들)

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 유지).

인덱스는 sparse하다 — 메모리 절약 설계

.index 파일은 모든 offset이 아니라 일부(sparse)만 기록한다. 예를 들어 offset 0, 100, 200, ... 식으로 일정 간격마다 "이 offset은 파일의 이 바이트 위치에 있다"는 매핑을 둔다. offset 153을 찾으면, 가장 가까운 아래 매핑(offset 100)으로 간 뒤 순차로 53개를 훑는다 — 순차 I/O라 빠르다.

이 sparse 설계가 왜 중요한가 — 인덱스를 매 offset마다 기록하면 인덱스 파일이 메시지 데이터만큼 커져서, 메모리에 올리기 어렵다. Kafka는 인덱스를 페이지 캐시에 올려두고 빠르게 조회하는 전략(장 01의 zero-copy와 같은 맥락)을 쓰므로, 인덱스가 작을수록 캐시 효율이 좋다. sparse는 공간-시간 트레이드오프에서 "조금의 순차 훑기 비용"으로 "훨씬 작은 인덱스"를 택한 설계다. index.interval.bytes(기본 4KB)로 간격을 조절한다 — 작게 두면 인덱스가 촘촘해지지만 파일이 커지고, 크게 두면 순차 훑기가 길어진다.

delete 정책 — 오래된 segment를 통째로 버린다

cleanup.policy=delete(기본)는 오래된 segment를 통째로 버린다. 두 기준:

  • 시간 기반: segment 내 가장 큰 timestampretention.ms(기본 7일 = 604800000ms)를 넘으면 그 segment 삭제.
  • 크기 기반: partition 크기가 retention.bytes(기본 -1 = 비활성) 한계를 넘으면 가장 오래된 segment부터 삭제.

둘 다 켜면 어떤 쪽이든 임계에 닿으면 삭제(먼저 닿는 쪽).

"먼저 닿는 쪽"이 만드는 실제 동작

시간과 크기를 둘 다 켜면 OR가 아니라 먼저 도달한 쪽이 삭제를 발동시킨다. 이걸 단일 조건으로 오해하면 보관 기간 추정이 틀린다 — 예를 들어 retention.ms=7일, retention.bytes=10GB를 둘 다 켰는데, partition당 처리량이 하루 5GB라면 2일 만에 10GB에 도달해 크기 조건이 먼저 발동한다. "7일 보관한다"고 생각했는데 실제론 2일 보관인 셈. 처리량이 높은 topic에선 크기 조건이 의도보다 일찍 데이터를 지운다.

반대로 처리량이 낮은 topic은 7일이 지나도 10GB에 못 미치므로 시간 조건이 발동한다. 따라서 retention은 "두 조건 중 먼저 닿는 쪽"이라는 점을 항상 명시해야 한다 — 보관 기간을 정확히 예측하려면 처리량·크기 한계·시간 한계를 함께 계산해야 한다.

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 단위 삭제"가 실제로 어떻게 일어나는지 보여준다.

시나리오의 핵심 디테일을 두 가지 더 짚자. 첫째, active segment는 절대 삭제 대상이 아니다 — 시각 1h에 rolling되기 전까지 segment-0은 active라서, 설령 retention.ms가 지나도 지워지지 않는다. rolling이 일어나야 비로소 삭제 후보가 된다. 그래서 "처리량이 낮아 active segment 하나에 메시지가 계속 append되는" topic은, retention.ms가 지나도 active segment가 안 지워져서 실제 보관 기간이 더 길어진다.

둘째, retention은 partition 단위로 적용된다. topic이 partition 3개면 각 partition이 독립적으로 retention을 계산한다. 한 partition은 7일이 지나 삭제됐어도, 다른 partition은 아직 rolling이 안 일어났으면 segment가 남아 있을 수 있다. consumer가 전체 topic을 볼 때 "일부 partition만 옛 데이터가 있고 일부는 없는" 불균형이 생길 수 있다 — partition별 처리량 차이가 retention 발동 시점을 다르게 만들기 때문이다.

compact 정책 — 같은 key의 최신값만 남긴다

cleanup.policy=compact는 key 기반이다. 같은 key의 메시지가 여러 개 있으면 가장 최근 것만 남기고 옛 것은 버린다 (design.md — Log Compaction).

예: topic에 user1의 프로필이 v1, v2, v3로 업데이트되며 들어온다. compaction 후엔 user1v3만 남는다. 과거(v1, v2)는 버려진다. 이게 "사용자 최신 상태만 유지"가 필요한 topic에 유용한 이유다.

compaction ≠ 압축

"compaction = 압축(zip)"이라고 생각하면 안 된다. 압축은 용량을 줄이지만 데이터는 그대로고, compaction은 옛 데이터 자체를 버린다. 용량은 줄지만 원리가 다르다.

compaction은 어떻게 돌아가나 — log cleaner

compaction은 log cleaner라는 백그라운드 스레드가 수행한다. broker 설정 log.cleaner.enable=true(기본)로 활성화된다. 작동 방식:

  1. cleaner는 active segment가 아닌 (이미 rolling된) segment들을 대상으로 compaction을 수행한다. active segment는 쓰기 진행 중이므로 건드리지 않는다.
  2. 각 key의 가장 최신 offset을 추적(offset map). 옛 segment에서 같은 key가 여러 번 나오면, 가장 마지막 것만 남기고 나머지는 버린다.
  3. 결과를 새 segment로 다시 써서, 옛 segment를 대체한다.

이 과정이 partition 전체를 한 번에 하는 게 아니라 segment 단위로 incremental하게 일어난다 — 그래서 compaction이 진행 중이어도 consumer는 정상적으로 읽을 수 있다. 단, compaction 직후에 offset이 연속이 아닐 수 있다(옛 key가 빠진 자리). consumer는 offset이 아니라 key로 데이터를 식별하므로 문제되지 않는다.

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)

tombstone이 "삭제"라는 게 바로 이해하기 tricky하다 — tombstone은 메시지 하나(value=null)를 produce하는 행위다. 즉 "삭제하려면 한 번 더 써라"는 역설. 그 이유는 compaction이 "key의 최신값을 남긴다"는 규칙에 있다 — value=null을 최신값으로 두면, "이 key의 최신값은 없음"이 돼서 과거 값과 함께 결국 사라진다. tombstone이 즉시 삭제가 아니라 "다음 compaction 때 삭제 예정" 표식인 이유도 여기에 있다.

dirty ratio — compaction 트리거

min.cleanable.dirty.ratio(기본 0.5) — 한 segment에서 옛 key 값이 일정 비율(dirty) 이상 쌓여야 compact 대상이 됨. 너무 낮으면 빈번 compact(CPU 비용), 너무 높으면 용량 비효율.

dirty ratio의 의미를 구체적으로 보자. segment에 key A가 100번 나온다고 하자. 이 중 최신 1개만 남아야 하므로 99개가 "dirty"(옛값)다. dirty ratio = 99/100 = 0.99. 기본값 0.5는 "segment의 절반 이상이 옛값이면 compact한다"는 뜻이다. 처리량이 높은 topic은 dirty 비율이 빨리 0.5에 도달하므로 compact가 자주 일어나고, 처리량이 낮으면 compact가 늦어진다.

log cleaner는 백그라운드 스레드(num.cleaning.threads, 기본 1)로 돈다. partition이 많고 compact 부하가 크면 스레드 수를 늘려 병렬 compact를 시킬 수 있지만, broker CPU 비용이 올라간다. compact가 I/O·CPU를 소모하므로, 운영 트래픽이 피크일 때는 compact throttle(log.cleaner.threads, log.cleaner.io.max.bytes.per.second)로 영향을 제한하는 것도 고려한다.

key가 없으면 compact는 무의미

key가 null인 메시지는 compact 대상이 아니다 → delete와 혼합(compact,delete) 운영이 권장된다.

이 함정이 자주 나타나는 패턴을 보자. 팀이 "사용자 활동 로그" topic을 만들고 compact를 켰다고 하자. 메시지에 key를 안 줬다(producer.send(new ProducerRecord("activity-log", event))). compact는 key 단위로 동작하므로, key가 null인 메시지는 전부 "compact 대상 아님"이 돼서 옛값이 안 지워진다. 결과적으로 compact를 켰는데도 디스크가 계속 찬다 — "왜 compact가 안 먹히지?"라는 의문이 나오는 지점이다.

해법은 둘 중 하나다:

  • key를 준다(예: 사용자 ID). 그래야 같은 key의 옛 이벤트가 compact 대상이 된다.
  • compact 대신 delete를 쓴다. 활동 로그는 "전체 이력을 시간순으로" 보는 용도이므로, compact보다 delete가 자연스럽다.

정책 선택 전에 "이 topic의 메시지에 key가 있는가, key가 의미가 있는가"를 먼저 점검하는 습관이 필요하다. key 없는 topic에 compact를 켜는 건 아무 효과가 없는 설정이다.

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

어느 정책을 택할까 — 결정 기준

선택은 "이 topic이 나중에 어떻게 읽힐 것인가"로 결정된다:

  • 전체 이력이 필요하다 (감사 로그, 분석 원시 데이터) → delete. "30일 치 전체 이력을 순서대로" 읽는 패턴.
  • key의 최신값만 필요하다 (사용자 프로필 현재 상태, config) → compact. "user1의 현재 설정"만 알면 되는 패턴.
  • key 최신값을 유지하되 너무 오래된 건 지운다 (상태 저장 + 히스토리 한계) → compact,delete. 가장 유연하지만 segment 관리 비용이 크다.

흔한 실수는 "compact = 용량 절약"으로 생각하고 무조건 compact를 켜는 것이다. 하지만 key 없이 produce하는 topic(대부분의 이벤트 로그)에서 compact는 아무 효과가 없다 — key가 null이면 compact 대상이 아니므로. 또 compact를 켜면 "옛 이력"이 사라지므로, replay로 과거를 분석하려는 용도에선 데이터가 부족해진다. 정책 선택은 "이 데이터를 나중에 어떻게 쓸 것인가"로부터 거꾸로 정해야 한다.

retention과 consumer — offset이 데이터보다 오래 살면?

retention이 메시지를 지우면, consumer 입장에서 한 가지 문제가 생긴다 — commit한 offset이 지워진 메시지를 가리킬 수 있다. 예를 들어 consumer가 offset 1000까지 commit했는데, retention에 의해 offset 800 이하가 지워졌다고 하자. consumer가 재시작해서 offset 1000부터 읽으려 하지만, 그 offset이 이미 없다.

Kafka는 이 상황을 auto.offset.reset 설정으로 처리한다:

  • offset이 없으면(지워졌거나 commit 이력이 없으면), auto.offset.reset=earliest면现存하는 가장 오래된 offset부터(여기선 801), latest면 현재 끝부터, none이면 예외.

이게 왜 중요한가 — retention이 짧고 consumer가 오래 죽어 있으면, 재시작 시 commit offset이 이미 지워진 영역을 가리킬 수 있다. earliest로 두면 "유실은 없지만 중복 가능(이미 처리했던 일부가 다시 옴)", latest로 두면 "중복은 없지만 유실 가능(죽은 동안 쌓인 데이터 스킵)". retention 기간과 consumer 장애 허용 시간의 관계를 설계 단계에서 맞춰야 한다 — "consumer가 하루 치 데이터를 처리할 동안 retention은 최소 2일" 같은 규칙이 그래서 나온다.

실제 시나리오 — retention과 offset이 충돌할 때

consumer가 offset 5000까지 commit하고 크래시 났다. 3일 동안 재시작을 안 했다. retention.ms=24시간이라, offset 0~6000이 지워지고现存하는 가장 오래된 offset이 6500이라고 하자. consumer가 재시작해서 offset 5000부터 읽으려 하지만, broker는 "그 offset은 없다"고 한다.

flowchart TD
    A["consumer 크래시 전<br/>commit offset=5000"] --> B["3일 대기"]
    B --> C["retention이 offset 0~6000 삭제<br/>现存 최소 offset=6500"]
    C --> D["consumer 재시작 → offset 5000 요청"]
    D --> E{"auto.offset.reset"}
    E -->|"earliest"| F["offset 6500부터 읽음<br/>(6000~6500 중복 처리 가능)"]
    E -->|"latest"| G["현재 끝부터 읽음<br/>(6000~끝까지 유실)"]
    E -->|"none"| H["예외 → consumer 종료"]

이 시나리오가 "retention 단축은 consumer 가용성과 엮여 있다"는 걸 보여준다. retention을 줄이면 디스크는 절약되지만, consumer 장애 허용 폭도 줄어든다. 둘의 균형을 잡는 게 retention 설계의 핵심이다.

tiered storage — 로컬 디스크 한계를 넘는 설계

retention으로 오래된 segment를 지우지 않고 보관하려면, broker 로컬 디스크가 계속 자라야 한다. 이건 비용·확장성 압박이 된다 — TB 단위의 히스토리를 가진 topic은 broker당 디스크를 크게 해야 하고, broker 추가 시 데이터 재분배 비용도 크다.

Tiered Storage(Kafka 3.6+, KIP-405)는 이 문제를 "오래된 segment를 원격 저장(S3 등)으로 옮기고, broker 로컬에는 최근 segment만 둔다"로 푼다. consumer가 오래된 offset을 요청하면 broker가 원격에서 가져와 반환한다. 로컬 디스크는 "핫 데이터"만 담으므로 broker 수를 늘리지 않고도 히스토리를 길게 보관할 수 있다.

Tiered Storage는 ASF core에 들어갔지만, 실제 원격 저장 백엔드(S3 등) 연동은 Confluent 등 벤더 구현이나 별도 플러그인이 필요한 경우가 많다. 설정(remote.log.storage.system.enable, remote.log.metadata.manager.class.name 등)은 1차 출처로 확인한다(미검증 항목 명시).

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

확인할 것: 1분이 지난 후 segment가 삭제됐는지(describe의 segment 수 감소, 또는 로그 디렉토리 파일 감소). 단, active segment는 retention 대상이 아니므로, 메시지를 적게 넣어 active segment 하나만 있으면 삭제가 안 보일 수 있다. retention이 발동하려면 rolling(segment 전환)이 일어나야 한다 — 큰 메시지를 여러 개 넣어 log.segment.bytes에 도달하게 하거나, log.segment.ms를 짧게 설정한 topic을 따로 만들어야 한다.

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·시간 조건 충족 시 실행).

retention과 offset reset의 상호작용

# 짧은 retention topic에서 메시지 produce 후 retention 대기
# 그 사이 consumer가 offset을 commit하지 않고 오래 대기
# retention이 offset 0~N을 지운 상태에서 consumer 시작

# auto.offset.reset=earliest로 두면现存하는 가장 오래된 offset부터
bin/kafka-console-consumer.sh --topic ret-demo --group g-late \
  --bootstrap-server localhost:9092 \
  --consumer-property auto.offset.reset=earliest

확인할 것: retention에 의해 옛 메시지가 지워진 상태에서 consumer가 시작하면, offset 0부터가 아니라现存하는 가장 오래된 offset부터 읽힌다. 이게 "retention vs consumer offset" 충돌의 실제 동작이다.

흔히 묻는 것, 흔히 틀리는 것

오해 정정
"compaction = 압축(zip)" 아님. key별 옛값 제거
"retention은 시간 OR 크기 중 하나만" 둘 다 켜면 먼저 닿는 쪽 삭제
"key 없이 produce해도 compact된다" key 없으면 compact 의미 없음
"메시지 단위로 삭제된다" segment 단위로 통째 삭제(delete 정책)
"active segment도 retention 대상" 아님. rolling 후 후보
"tombstone은 영구 남는다" 일정 기간 후 제거됨
"retention만 짧게 두면 consumer 장애해도 안전하다" 아니다. consumer가 retention 기간 넘게 죽어 있으면 commit offset이 지워진 영역을 가리켜 유실/중복
"tiered storage는 retention을 안 해도 된다는 뜻" 아니다. 원격 저장이 커지는 건 여전히 비용. tiered storage는 로컬 디스크 압박을 줄일 뿐

더 깊이

  • __consumer_offsets는 왜 compact인가: consumer offset은 (group,topic,partition)을 key로 저장. 같은 key의 옛 offset은 필요 없음 → compact로 최신만 유지(장 04).
  • tiered storage (3.6+): 로컬 디스크 외 원격(S3)에 오래된 segment 보관 → 비용 절감. KIP-405. 단, 백엔드 연동은 벤더/플러그인 의존(미검증).
  • log.segment.ms: 시간 기반 segment rolling. 기본값은 7일(미검증). 짧게 두면 segment 수가 많아져 파일 핸들 증가.
  • max.compaction.lag.ms: 메시지가 이 시간 이상 오래됐으면 dirty ratio와 무관하게 compaction 대상이 되도록 강제. "최대 이 시간 내에는 compaction이 일어난다"는 보장.
  • min.compaction.lag.ms: 메시지가 produce된 후 이 시간 동안은 compaction 대상이 되지 않도록 지연. "너무 빨리 compact해서 최신 이력이 날아가는 것"을 막는다.
  • delete.retention.ms(compact 정책 전용): tombstone을保留하는 시간(기본 24시간). 이 기간 동안은 tombstone이 남아 있어 "이 key가 삭제됐음"을 consumer가 알 수 있다. 너무 짧으면 tombstone이 빨리 사라져서 늦게 시작한 consumer가 삭제 사실을 모른다.

요약 — 이 글의 결론

  • segment: partition을 이루는 파일 단위(.log + .index + .timeindex). active segment만 쓰기. 인덱스는 sparse하여 페이지 캐시에서 빠르게 조회.
  • delete: 시간(retention.ms, 기본 7일) 또는 크기(retention.bytes) — 먼저 도달한 쪽. segment 통째 삭제.
  • compact: key별 최신값만 유지. tombstone(null)으로 key 삭제. key 없으면 무의미. (압축 아님!) log cleaner가 백그라운드에서 segment 단위로 수행.
  • compact,delete: 상태 유지 + 기간 제한 결합.
  • 설정: topic별로 동적 변경(kafka-configs.sh --alter --add-config).
  • retention vs consumer: retention이 옛 메시지를 지우면 consumer의 commit offset이 없는 영역을 가리킬 수 있다 → auto.offset.reset이 동작.
  • tiered storage (3.6+): 오래된 segment를 원격 저장으로 옮겨 로컬 디스크 압박을 줄임.
  • delete vs compact는 "무엇을" 지울지의 차이 — 시간/크기(delete) vs key별 옛값(compact).

생각해 볼 문제

  1. delete와 compact의 차이를 "무엇을 지우는가" 관점에서 설명하라.
  2. retention 시간과 크기를 둘 다 켜면 어떻게 동작하는가? "먼저 닿는 쪽"이 왜 헷갈리기 쉬운가?
  3. segment를 통째로 삭제하는 이유(append-only)를 설명하라.
  4. compaction에서 tombstone(value=null)의 역할은? 어떻게 쓰는가? "삭제하려면 한 번 더 쓴다"는게 왜 역설적인가?
  5. key 없이 produce하는데 cleanup.policy=compact인 topic에서 무슨 일이 벌어지는가?
  6. __consumer_offsets가 compact 정책을 쓰는 이유는?
  7. retention.ms를 topic별로 동적 변경하는 명령은?
  8. 인덱스가 sparse한 이유는? dense 인덱스를 쓰면 어떤 문제가 생기나?
  9. retention이 옛 메시지를 지웠는데 consumer의 commit offset이 그 영역을 가리키면 어떻게 되나? auto.offset.reset이 어떻게 개입하나?
  10. Tiered Storage가 로컬 디스크 압박을 어떻게 줄이는가? 단점은?

참고

'Data & Platform > Event Streaming' 카테고리의 다른 글

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