Page Cache 활용
Kafka는 OS의 Page Cache를 적극 활용하여 높은 처리량과 낮은 지연시간을 달성합니다.
Page Cache 개념
정의
Page Cache는 운영체제가 디스크 I/O를 최적화하기 위해 메모리에 유지하는 캐시입니다.
┌─────────────────────────────────────────────────────────────────┐
│ Memory Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ Application │ ← Kafka Broker (JVM Heap) │
│ │ Memory │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Page Cache │ ← OS가 관리하는 파일 캐시 │
│ │ (OS Memory) │ 디스크 데이터의 메모리 복사본 │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Disk │ ← 실제 저장소 │
│ │ (.log files) │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Kafka의 Page Cache 활용 전략
전통적인 메시징 시스템:
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Produce │───>│ In-Memory │───>│ Disk │
│ │ │ Queue │ │ (백업) │
└───────────┘ └───────────┘ └───────────┘
↓
JVM 힙 메모리 사용
GC 오버헤드 발생
Kafka:
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Produce │───>│Page Cache │───>│ Disk │
│ │ │ (OS 관리) │ │ (log file)│
└───────────┘ └───────────┘ └───────────┘
↓
OS가 캐시 관리
JVM GC 영향 없음
쓰기 경로 (Write Path)
Producer → Broker
1. Producer가 메시지 전송
↓
2. Broker가 메시지 수신
↓
3. write() 시스템 콜
↓
4. 커널이 Page Cache에 쓰기 (메모리)
↓
5. 즉시 ACK 반환 (async)
↓
6. OS가 백그라운드에서 디스크 flush
Write-behind 캐싱
// Page Cache → Disk flush 간격
// OS 커널 파라미터로 제어
vm.dirty_ratio = 20 // 전체 메모리의 20%가 dirty일 때 flush
vm.dirty_background_ratio = 5 // 5%가 dirty일 때 백그라운드 flush 시작
vm.dirty_expire_centisecs = 3000 // 30초 이상 된 dirty 페이지 flushKafka 설정
// 메시지를 디스크에 동기화하는 주기
log.flush.interval.messages = 10000 // N개 메시지마다 flush
log.flush.interval.ms = 1000 // N ms마다 flush
// 일반적으로 기본값(무제한) 사용 권장
// OS의 Page Cache가 더 효율적으로 관리읽기 경로 (Read Path)
Consumer ← Broker
시나리오 1: 최신 메시지 읽기 (Cache Hit)
┌───────────┐ ┌───────────┐
│ Consumer │<───│Page Cache │ ← 메모리에서 직접 읽기
│ │ │ (HIT!) │ 디스크 I/O 없음
└───────────┘ └───────────┘
시나리오 2: 과거 메시지 읽기 (Cache Miss)
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Consumer │<───│Page Cache │<───│ Disk │
│ │ │ (MISS) │ │ │
└───────────┘ └───────────┘ └───────────┘
↑
디스크에서 로드 후 캐시
순차 읽기 최적화
┌─────────────────────────────────────────────────────────────────┐
│ Sequential Read Optimization │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Consumer가 순차적으로 읽을 때: │
│ │
│ 요청: offset 1000 │
│ OS Readahead: offset 1000 ~ 2000 미리 로드 │
│ │
│ Page Cache: │
│ ┌────┬────┬────┬────┬────┬────┬────┬────┐ │
│ │1000│1001│1002│...│1998│1999│2000│ │ │
│ └────┴────┴────┴────┴────┴────┴────┴────┘ │
│ ↑ ↑ │
│ 요청됨 Prefetch됨 │
│ │
└─────────────────────────────────────────────────────────────────┘
Producer-Consumer 파이프라인
실시간 처리 시나리오
Timeline:
┌───────────────────────────────────────────────────────────────┐
│ │
│ t=0 Producer write → Page Cache │
│ t=1 Consumer read ← Page Cache (same data!) │
│ │
│ 결과: 디스크 I/O 없이 메모리에서 직접 전달 │
│ │
└───────────────────────────────────────────────────────────────┘
조건:
- Consumer가 Producer를 closely 따라가는 경우
- Consumer Lag이 적은 경우
- Page Cache가 충분한 경우
지연된 소비 시나리오
Timeline:
┌───────────────────────────────────────────────────────────────┐
│ │
│ t=0 Producer write → Page Cache │
│ ... (시간 경과, 새 데이터로 캐시 교체) │
│ t=1000 Consumer read ← Disk (cache evicted) │
│ │
│ 결과: 디스크 읽기 필요 │
│ │
└───────────────────────────────────────────────────────────────┘
발생 조건:
- Consumer Lag이 큰 경우
- 과거 데이터 재처리
- Page Cache 부족
메모리 할당 전략
권장 메모리 비율
┌─────────────────────────────────────────────────────────────────┐
│ 서버 메모리 할당 예시 (64GB) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ │
│ │ JVM Heap │ 6GB (10%) - Broker 애플리케이션 │
│ ├──────────────────┤ │
│ │ Page Cache │ 50GB+ (80%) - 로그 파일 캐싱 │
│ ├──────────────────┤ │
│ │ OS + Others │ 8GB (10%) - 운영체제, 네트워크 버퍼 │
│ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
JVM 힙 크기 설정
# Kafka Broker 시작 시
export KAFKA_HEAP_OPTS="-Xmx6g -Xms6g"
# 힙이 너무 크면:
# - Page Cache 공간 부족
# - GC 시간 증가
# - 전체 성능 저하Page Cache 모니터링
시스템 메트릭
# 메모리 사용량 확인
free -h
# 예시 출력:
# total used free shared buff/cache available
# Mem: 62Gi 5.2Gi 2.1Gi 1.0Mi 55Gi 56Gi
# buff/cache: Page Cache로 사용 중인 메모리vmstat로 I/O 모니터링
vmstat 1
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
# r b swpd free buff cache si so bi bo in cs us sy id wa st
# 1 0 0 2100000 100000 55000000 0 0 100 5000 1000 2000 5 2 93 0 0
# bi: blocks read from disk
# bo: blocks written to disk
# 낮을수록 Page Cache 효과적Kafka 메트릭
# JMX 메트릭
kafka.log:type=Log,name=LogFlushRateAndTimeMs
kafka.log:type=Log,name=Size
성능 최적화
OS 튜닝
# /etc/sysctl.conf
# Page Cache flush 임계값
vm.dirty_ratio = 80
vm.dirty_background_ratio = 5
# Readahead 최적화
blockdev --setra 4096 /dev/sda
# Swappiness 최소화 (Page Cache 유지)
vm.swappiness = 1파일시스템 선택
# XFS 권장 (ext4도 가능)
# Kafka 로그 디렉토리 마운트 옵션
/dev/sdb1 /kafka-logs xfs defaults,noatime 0 0
# noatime: 접근 시간 업데이트 비활성화 (성능 향상)RAID 구성
RAID 10 권장:
- 높은 읽기/쓰기 성능
- 디스크 장애 대응
┌─────────────────────────────────────────┐
│ RAID 10 │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Disk1│ │Disk2│ │Disk3│ │Disk4│ │
│ │Mirror│ │Mirror│ │Mirror│ │Mirror│ │
│ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │
│ └───┬───┘ └───┬───┘ │
│ └─────┬───────┘ │
│ Stripe │
└─────────────────────────────────────────┘
Page Cache와 복제
Follower의 Page Cache 활용
Leader Broker Follower Broker
┌─────────────┐ ┌─────────────┐
│ Page Cache │ │ Page Cache │
│ (Write) │──────>│ (Write) │
└─────────────┘ └─────────────┘
↑
Fetch 요청으로 복제
Follower도 Page Cache에 쓰기
→ Follower에서 읽는 Consumer도 Cache 활용
Fetch from Follower (Kafka 2.4+)
// Consumer가 Follower에서 읽기 가능
// 가까운 Replica에서 읽어 네트워크 지연 감소
props.put("client.rack", "rack-a");
// Broker 설정
replica.selector.class = org.apache.kafka.common.replica.RackAwareReplicaSelector
댓글 (0)