콘텐츠로 이동
Study NoteKafka

2. 로그라는 자료구조

Kafka의 모든 동작은 “추가만 되는 파일에 순서대로 쓴다”는 한 문장에서 파생된다

이 장에서 처음 나오는 말5개
레코드Record
Kafka에 쓰는 데이터 한 건. 키 · 값 · 타임스탬프 · 헤더로 이루어진다. "메시지"와 같은 말이다.
토픽Topic
레코드가 쌓이는 논리적 이름. "orders.created"처럼 이벤트 종류 하나가 토픽 하나가 되는 것이 보통이다.
파티션Partition
토픽을 물리적으로 쪼갠 조각. 실제 로그 파일은 파티션 단위이고, 순서 · 병렬성 · 분산이 전부 이 단위로 결정된다.
오프셋Offset
파티션 안에서 레코드마다 붙는 순번(0, 1, 2…). 소비자는 이 번호를 책갈피 삼아 "어디까지 읽었는지"를 관리한다.
세그먼트Segment
파티션 로그를 디스크에서 나눠 담는 파일 단위. 보존 기간이 지난 데이터는 세그먼트 파일째 지워진다.

레코드 — 쌓이는 데이터의 단위

섹션 제목: “레코드 — 쌓이는 데이터의 단위”
필드내용왜 있나
key바이트열 (없어도 됨)파티션 배정 기준 — 같은 키는 같은 파티션으로 간다 (아래)
value바이트열본문. 직렬화 형식(JSON · Avro …)은 Kafka가 관여하지 않는다 — 클라이언트끼리의 계약이다(7장)
timestamp생성 또는 적재 시각시간 기준 보존 · 시간으로 오프셋 찾기
headers키-값 메타데이터추적 ID 등, 본문을 열지 않고 볼 부가 정보

브로커에게 value는 그냥 바이트열이다 — 내용을 해석하지도, 검사하지도 않는다. “브로커는 단순하게”(0장)의 한 단면이고, 형식 계약이 왜 별도 도구 (스키마 레지스트리, 8장)로 필요한지의 이유다.

토픽은 이름일 뿐, 실제로 존재하는 것은 파티션이다. 토픽 하나는 파티션 여러 개로 쪼개지고, 각 파티션이 독립된 append-only 로그다.

토픽 하나가 파티션 셋으로 나뉘고 프로듀서가 세 파티션에 나눠 쓰는 구조

왜 쪼개는가 —

파티션이 주는 것어떻게
병렬성파티션마다 컨슈머가 하나씩 붙어 동시에 읽는다 (5장)
분산파티션들이 여러 브로커에 흩어져, 토픽 하나가 브로커 한 대의 용량을 넘을 수 있다 (3장)
순서의 경계순서는 파티션 안에서만 보장된다 — 파티션 사이의 순서는 정의되지 않는다

키 — 어느 파티션으로 갈지 정하는 것

섹션 제목: “키 — 어느 파티션으로 갈지 정하는 것”

프로듀서는 레코드를 보낼 때 파티션을 이렇게 고른다 —

레코드에 키가 있으면 해시로 파티션이 정해지고, 없으면 스티키 분배로 배치 단위로 돌아가며 채우는 분기
  • 키가 있으면 — 같은 키는 항상 같은 파티션 → 그 키의 레코드끼리는 쓴 순서대로 읽힌다. “주문 ID를 키로” 하면 한 주문의 이벤트들은 순서가 지켜진다
  • 키가 없으면 — 골고루 분배될 뿐 순서 개념이 없다. 순서가 무관한 데이터(로그 · 메트릭)에 쓴다

파티션 안에서 레코드는 오프셋(0, 1, 2…)으로 번호가 붙는다. 핵심은 두 위치가 서로 독립이라는 것 —

  • 브로커가 지우는 위치 — 보존 정책(기간 · 용량)에 따라 오래된 쪽부터 지운다. 누가 읽었는지는 전혀 고려하지 않는다
  • 소비자가 읽는 위치 — 소비자(그룹)마다 따로 관리한다. 읽어도 데이터는 그대로다

이 독립성에서 Kafka다운 동작이 전부 나온다 —

할 수 있는 일왜 가능한가
소비자 그룹 여러 개가 같은 토픽을 각자 읽기그룹마다 오프셋이 따로다
장애 복구 후 밀린 곳부터 이어 읽기마지막 커밋 오프셋이 남아 있다
버그 수정 후 어제 데이터 재처리오프셋을 되감으면 된다 — 데이터가 아직 있으니까
새 소비자가 처음부터 따라잡기보존된 범위의 맨 앞(earliest)부터 읽으면 된다

“디스크에 쓰는데 왜 빠른가”는 Kafka 도입 논의에서 꼭 나오는 질문이다.

  • 파티션 로그는 세그먼트 파일들로 나뉘어 저장된다. 쓰기는 항상 마지막(active) 세그먼트 끝에 순차 추가 — 디스크가 가장 잘하는 접근 패턴이다. 랜덤 I/O가 없다
  • 삭제도 세그먼트 파일째 지운다 — 레코드 단위로 지우지 않으니 삭제 비용이 없다. 큐가 메시지 단위 삭제를 관리하는 것과 대비되는 지점이다
  • 읽기는 OS 페이지 캐시를 그대로 타고, 네트워크 전송은 제로카피(sendfile)로 나간다. 최근 데이터를 읽는 소비자는 사실상 메모리에서 읽는다 — 브로커 메모리 계획에서 힙보다 페이지 캐시 몫이 중요한 이유다(9장)
  • 실체는 파티션이다 — 순서 · 병렬성 · 분산의 단위이고, 순서는 파티션 안에서만 보장된다
  • 같은 키 → 같은 파티션 → 순서 보장. 순서가 필요한 단위가 곧 키다
  • 브로커가 지우는 위치(보존)와 소비자가 읽는 위치(오프셋)는 독립 — 다중 구독 · 재처리의 근거
  • 파티션 수를 늘리면 키 배정이 바뀐다 — 처음 정할 때 신중히(7장)
  • 빠른 이유는 순차 쓰기 · 세그먼트째 삭제 · 페이지 캐시