Topic & Partition Internals
파티션은 하나의 거대한 파일이 아닌, 로그 세그먼트(Log Segment) 라는 여러 파일들의 집합으로 구성된 디렉터리이며, 이 설계가 카프카의 빠른 입출력과 효율적인 데이터 관리를 가능하게 한다.
파티션의 물리적 구조
Section titled “파티션의 물리적 구조”하나의 파티션은 브로커의 파일 시스템 내에서 토픽명-파티션번호 형태의 디렉터리로 표현되며, 내부에는 파티션의 전체 데이터를 조각내어 저장하는 다수의 로그 세그먼트 파일들이 존재한다.
- 경로 예시: Kafka 데이터 디렉터리 내
/my-topic-0,/my-topic-1등 - 구성: 각 디렉터리 안에는
00000000000000000000.log,00000000000000000000.index와 같은 파일들이 쌍으로 존재- 이 파일 이름의 숫자는 해당 세그먼트의 시작 오프셋을 의미
로그 세그먼트의 구성
Section titled “로그 세그먼트의 구성”세그먼트는 데이터 관리의 기본 단위로, 현재 쓰기 작업이 일어나는 단 하나의 활성 세그먼트와, 읽기만 가능한 이전 세그먼트들로 구성되며, 각 세그먼트는 다음 파일들로 이루어져 있다.
.log파일(데이터 파일)- 실제 메시지 데이터(키, 값, 타임스탬프, 헤더 등)가 저장되는 파일
- 데이터는 파일의 끝에만 추가되는 Append-Only 방식으로 기록
- 디스크 헤드의 움직임을 최소화하는 순차 쓰기(Sequential I/O)를 유발하여 매우 빠른 쓰기 성능 보장
.index파일(오프셋 인덱스).log파일에서 특정 오프셋의 위치를 빠르게 찾기 위한 인덱스 파일- 모든 메시지의 오프셋을 저장하는 것이 아니라, 일정 간격마다 (상대 오프셋, 물리적 파일 위치) 쌍을 저장하는 구조
- 컨슈머가 특정 오프셋을 요청하면, 카프카는 이 인덱스를 통해 대략적인 위치를 신속하게 찾은 후 해당 위치부터 순차적으로 스캔하여 메시지 조회
- 전체 파일을 스캔하는 비효율을 방지
.timeindex파일(타임스탬프 인덱스)- 오프셋 인덱스와 유사하게, (타임스탬프, 상대 오프셋) 쌍을 저장하여 시간 기반의 데이터 검색을 효율적으로 처리할 수 있도록 지원
고성능의 원리
Section titled “고성능의 원리”위와 같은 내부 구조 덕분에 카프카의 고성능을 이끌어내는 여러 최적화가 가능하다.
- 쓰기 성능: 순차 I/O
- 모든 쓰기 작업은 활성 세그먼트의 끝에 데이터를 추가하는 단순한 작업
- 디스크에 가해지는 부하가 가장 적은 순차 I/O이므로, 카프카는 높은 쓰기 처리량을 유지 가능
- 읽기 성능: 페이지 캐시 / 제로 카피
- 데이터 캐싱을 애플리케이션(JVM) 레벨이 아닌, 운영체제(OS)의 페이지 캐시(Page Cache)를 활용
- 조회한 데이터는 메모리(RAM)의 페이지 캐시에 저장되어, 이후 동일 데이터에 대한 요청은 디스크를 거치지 않고 메모리에서 직접 처리
- 페이지 캐시에 있는 데이터를 컨슈머에게 전송할 때 제로 카피(Zero-Copy) 기술 사용
- 이는 S 커널 영역에서 애플리케이션 영역으로 데이터를 복사하는 과정을 생략하고, 커널 버퍼에서 네트워크 버퍼로 데이터를 직접 전송하는 방식
- 데이터 복사와 CPU 컨텍스트 스위칭을 줄여 읽기 성능 향상
- 데이터 캐싱을 애플리케이션(JVM) 레벨이 아닌, 운영체제(OS)의 페이지 캐시(Page Cache)를 활용
로그 관리의 효율성
Section titled “로그 관리의 효율성”세그먼트 단위의 구조는 대용량 로그를 관리에도 큰 이점을 제공한다.
- 로그 보존(Log Retention)
- 오래된 데이터를 삭제할 때, 특정 레코드를 찾아 지우는 복잡한 과정 대신 보존 기간(
retention.ms)이나 용량(retention.bytes)이 지난 로그 세그먼트 파일 자체 삭제 - 파일 단위의 삭제는 OS에서 빠르게 처리할 수 있는 작업
- 오래된 데이터를 삭제할 때, 특정 레코드를 찾아 지우는 복잡한 과정 대신 보존 기간(
- 로그 압축(Log Compaction)
- 시간이 아닌 메시지의 키(Key)를 기준으로 데이터를 보존하는 정책
- 로그 압축은 각 키에 대해 가장 최신 버전의 값만 남기고 이전 버전의 값들을 제거
- 특정 키의 최신 상태를 추적해야 하는 경우(예: 사용자의 마지막 프로필 정보, 상품의 현재 가격)에 유용