Log Segment와 파일 구조
Kafka는 메시지를 디스크에 효율적으로 저장하기 위해 Log Segment 구조를 사용합니다.
디렉토리 구조
기본 레이아웃
/kafka-logs/ # log.dirs 설정
├── my-topic-0/ # {topic}-{partition}
│ ├── 00000000000000000000.log # 데이터 파일
│ ├── 00000000000000000000.index # 오프셋 인덱스
│ ├── 00000000000000000000.timeindex # 타임스탬프 인덱스
│ ├── 00000000000005242880.log # 다음 세그먼트
│ ├── 00000000000005242880.index
│ ├── 00000000000005242880.timeindex
│ ├── leader-epoch-checkpoint
│ └── partition.metadata
├── my-topic-1/
│ └── ...
└── __consumer_offsets-0/ # 내부 토픽
└── ...
파일명 규칙
파일명은 해당 세그먼트의 시작 오프셋을 20자리로 표현합니다.
00000000000000000000.log → 오프셋 0부터 시작
00000000000005242880.log → 오프셋 5,242,880부터 시작
00000000000010485760.log → 오프셋 10,485,760부터 시작
Log Segment 구조
세그먼트 구성 요소
┌─────────────────────────────────────────────────────────────────┐
│ Log Segment │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ .log 파일 │ │ .index 파일 │ │ .timeindex │ │
│ │ (데이터) │ │ (오프셋 인덱스) │ │ (시간 인덱스) │ │
│ └──────────────────┘ └──────────────────┘ └───────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ .txnindex 파일 │ │ .snapshot 파일 │ │
│ │ (트랜잭션 인덱스) │ │ (Producer 상태) │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Active Segment vs Inactive Segment
Partition Log:
┌────────────┬────────────┬────────────┬────────────┐
│ Segment 0 │ Segment 1 │ Segment 2 │ Segment 3 │
│ (Inactive) │ (Inactive) │ (Inactive) │ (Active) │
│ 읽기 전용 │ 읽기 전용 │ 읽기 전용 │ 쓰기 가능 │
└────────────┴────────────┴────────────┴────────────┘
↑
새 메시지 추가
.log 파일 (데이터 파일)
Record Batch 구조
┌─────────────────────────────────────────────────────────────────┐
│ .log 파일 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │Record Batch │ │Record Batch │ │Record Batch │ ... │
│ │ (배치 1) │ │ (배치 2) │ │ (배치 3) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Record Batch 상세
Record Batch:
┌─────────────────────────────────────────────────────────────────┐
│ Base Offset (8 bytes) - 배치의 첫 번째 오프셋 │
│ Batch Length (4 bytes) - 배치 크기 │
│ Partition Leader Epoch (4 bytes) │
│ Magic (1 byte) - 메시지 포맷 버전 │
│ CRC (4 bytes) - 체크섬 │
│ Attributes (2 bytes) - 압축, 타임스탬프 타입 등 │
│ Last Offset Delta (4 bytes) - 배치 내 마지막 오프셋 델타 │
│ First Timestamp (8 bytes) │
│ Max Timestamp (8 bytes) │
│ Producer ID (8 bytes) │
│ Producer Epoch (2 bytes) │
│ Base Sequence (4 bytes) │
│ Records Count (4 bytes) │
├─────────────────────────────────────────────────────────────────┤
│ Records (가변 길이) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Record │ │ Record │ │ Record │ ... │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────────────┘
개별 Record 구조
Record:
┌─────────────────────────────────────────────────────────────────┐
│ Length (varint) - 레코드 크기 │
│ Attributes (1 byte) │
│ Timestamp Delta (varint) - Base Timestamp 대비 차이 │
│ Offset Delta (varint) - Base Offset 대비 차이 │
│ Key Length (varint) │
│ Key (가변) │
│ Value Length (varint) │
│ Value (가변) │
│ Headers Count (varint) │
│ Headers (가변) │
└─────────────────────────────────────────────────────────────────┘
.index 파일 (오프셋 인덱스)
구조
.index 파일:
┌──────────────────────────────────┐
│ Relative Offset │ Position │
│ (4 bytes) │ (4 bytes) │
├──────────────────────────────────┤
│ 0 │ 0 │ → 오프셋 0 → 파일 위치 0
│ 100 │ 15240 │ → 오프셋 100 → 파일 위치 15240
│ 200 │ 30480 │ → 오프셋 200 → 파일 위치 30480
│ ... │ ... │
└──────────────────────────────────┘
Sparse Index
모든 메시지가 아닌 일정 간격으로 인덱스를 생성합니다.
// 인덱스 간격 설정 (기본 4KB)
log.index.interval.bytes = 4096메시지 검색 과정
오프셋 150 검색:
1. .index에서 이진 검색 → (100, 15240) 찾음
2. .log 파일의 15240 위치로 이동
3. 순차적으로 읽으며 오프셋 150 찾기
┌─────────────────────────────────────────┐
│ Index: 0→0, 100→15240, 200→30480 │
│ ↓ │
│ 오프셋 150 요청 → 100에서 시작하여 순차 검색│
└─────────────────────────────────────────┘
.timeindex 파일 (시간 인덱스)
구조
.timeindex 파일:
┌──────────────────────────────────────────┐
│ Timestamp │ Relative Offset │
│ (8 bytes) │ (4 bytes) │
├──────────────────────────────────────────┤
│ 1699000000000 │ 0 │
│ 1699000060000 │ 500 │
│ 1699000120000 │ 1000 │
│ ... │ ... │
└──────────────────────────────────────────┘
시간 기반 검색
// 특정 시간 이후의 메시지 검색
Map<TopicPartition, Long> timestamps = new HashMap<>();
timestamps.put(partition, targetTimestamp);
Map<TopicPartition, OffsetAndTimestamp> offsets =
consumer.offsetsForTimes(timestamps);Segment 관리
새 Segment 생성 조건
// 크기 기반 (기본 1GB)
log.segment.bytes = 1073741824
// 시간 기반 (기본 7일)
log.roll.ms = 604800000
// 또는
log.roll.hours = 168Segment 롤링 과정
1. Active Segment가 조건 충족
└── segment.bytes 초과 OR roll.ms 경과
2. 현재 Segment를 Inactive로 전환
3. 새 Segment 생성 (다음 오프셋으로 시작)
4. 새 Segment가 Active가 됨
관련 설정
| 설정 | 기본값 | 설명 |
|---|---|---|
log.segment.bytes | 1GB | 세그먼트 최대 크기 |
log.roll.ms | 7일 | 세그먼트 롤링 주기 |
log.index.size.max.bytes | 10MB | 인덱스 파일 최대 크기 |
log.index.interval.bytes | 4KB | 인덱스 간격 |
파일 핸들링
Memory-Mapped Files
┌─────────────────────────────────────────────────────────────────┐
│ Memory-Mapped I/O │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Application Memory OS Page Cache Disk │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────┐ │
│ │ MappedBuffer │ ←───→ │ .index 파일 │ ←→│ Disk │ │
│ │ (가상 메모리) │ │ .timeindex │ │ │ │
│ └───────────────┘ └───────────────┘ └───────────┘ │
│ │
│ 인덱스 파일은 mmap으로 매핑되어 빠른 접근 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
파일 디스크립터 관리
// 열린 파일 핸들러 수 제한
// 시스템 ulimit 설정 필요
ulimit -n 100000데이터 확인 도구
kafka-dump-log.sh
# 로그 파일 덤프
kafka-dump-log.sh \
--files /kafka-logs/my-topic-0/00000000000000000000.log \
--print-data-log
# 인덱스 파일 덤프
kafka-dump-log.sh \
--files /kafka-logs/my-topic-0/00000000000000000000.index \
--print-data-log
# 특정 오프셋 범위만
kafka-dump-log.sh \
--files /kafka-logs/my-topic-0/00000000000000000000.log \
--offset-position 1000:2000출력 예시
Dumping /kafka-logs/my-topic-0/00000000000000000000.log
Starting offset: 0
baseOffset: 0 lastOffset: 4 count: 5 baseSequence: -1 lastSequence: -1
producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0
isTransactional: false isControl: false position: 0
CreateTime: 1699000000000 size: 150 magic: 2
compresscodec: NONE crc: 1234567890
| offset: 0 CreateTime: 1699000000000 keysize: 3 valuesize: 5 key: key value: value
| offset: 1 CreateTime: 1699000001000 keysize: 3 valuesize: 5 key: key value: value
...
댓글 (0)