백엔드 데이터 인프라 85편. Kafka Design Efficiency — *마지막 한 방울 짜내기* 기법. sendfile() 시스템 콜 기반 zero-copy 메커니즘·MessageSet batching·end-to-end 압축 (gzip·snappy·zstd·lz4) 선택 가이드까지 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 85편이에요. 84편 에서 디스크 sequential I/O (순차 읽기·쓰기) 의 비밀을 잡았다면, 이번 85편은 마지막 한 방울 짜내기 기법 — Efficiency. Zero-copy·batching·compression 세 가지 무기.
Efficiency가 어렵게 느껴지는 이유
Persistence 까지는 디스크 활용 패턴 이었는데, Efficiency 는 OS (운영체제) 시스템 콜·CPU·네트워크 트레이드오프 같은 낮은 레벨 영역이에요.
첫째, Zero-copy 가 뭔지 가 안 잡힙니다. 왜 copy 가 비싸고 어떻게 zero 가 되는지 가 OS 레벨에서 흐름을 그려봐야 잡혀요.
둘째, 압축 알고리즘 4가지 (gzip·snappy·zstd·lz4) 중 언제 어느 걸 골라야 하는지 가 명확하지 않습니다.
이 글에서 Kafka 의 3가지 효율 기법·각각의 정확한 메커니즘·운영 선택까지 짚어볼게요.
Zero-Copy — sendfile() 시스템 콜
전통적 데이터 전송 — 4번 copy
Consumer 가 디스크 파일을 읽어 네트워크로 보내는 가장 흔한 시나리오:
1. 디스크 → kernel buffer (DMA copy)
2. kernel buffer → user buffer (Kafka 프로세스)
3. user buffer → kernel socket buffer
4. kernel socket buffer → NIC (DMA copy)
4번 copy + 2번 context switch (user ↔ kernel, 커널·사용자 영역 사이 문맥 전환). DMA (Direct Memory Access, CPU 없이 메모리 직접 전송) 가 첫·마지막 단계를 맡아도 중간 두 단계는 CPU 가 직접 옮겨야 하고, 각 copy 와 switch 마다 CPU 사이클·메모리 대역폭 이 갈려요.
Zero-Copy — 1번 copy
Linux 의 sendfile() 시스템 콜 사용:
1. 디스크 → kernel buffer
2. kernel buffer → NIC (DMA, kernel 안에서)
user space 거치지 않음. CPU 사용 최소화, 메모리 대역폭도 대폭 절약.
Java 에서 사용
// java.nio.channels.FileChannel.transferTo()
fileChannel.transferTo(position, count, socketChannel);
내부적으로 sendfile() 을 부릅니다. Kafka 가 consumer 에게 메시지 전송 시 이 메커니즘으로 흘려요.
효과
벤치마크에서 sendfile 사용 시 처리량 60~70% 향상. consumer 가 많을수록 효과가 커져요.
여기서 시험 함정이 하나 있어요 — TLS (Transport Layer Security, 전송 암호화) 활성 시 zero-copy 효과 감소. TLS 암호화는 user space CPU 처리 가 필요해서 kernel zero-copy 우회가 불가능해요. 대규모 컨슈머 환경 + TLS 면 별도 kernel TLS (kTLS, 커널에서 암호화) 검토가 필요합니다.
Batching — MessageSet
단일 메시지 vs Batch
Producer 가 메시지 1개씩 보내면 TCP (Transmission Control Protocol, 네트워크 전송 프로토콜) overhead 도 N 번, 디스크 syscall (시스템 호출) 도 N 번, 압축 CPU 도 N 번 들어요.
Batch 모드는 수십~수백 메시지를 MessageSet (메시지 묶음) 으로 한꺼번에 처리. TCP 도 한 번, 디스크도 한 번에 write, 압축도 MessageSet 전체 한 번. 결과는 처리량 10~100배.
Producer 측 설정
linger.ms=10 # 최대 10ms 대기 후 batch 전송
batch.size=16384 # 또는 16KB 채워지면 즉시 전송
compression.type=lz4
linger.ms (전송 직전 대기 시간) 가 지연 vs 처리량 trade-off 의 핵심. 0 이면 즉시 (낮은 지연), 큰 값이면 큰 batch (높은 처리량).
Broker 측
Broker 가 MessageSet 을 통째로 디스크에 저장. consumer 에게 전송할 때도 통째로 (zero-copy).
같은 MessageSet 이 write·read·network 모두 통과
핵심 — producer 가 만든 batch 가 broker 디스크·consumer 까지 같은 형식 으로 흐릅니다. 중간에 재batch·재압축 없음.
한 줄 정리 — Producer batch = broker storage = consumer delivery. 한 번 batching = 모든 단계 효율.
Compression — 4가지 알고리즘
Producer 가 MessageSet 압축 → broker 저장·전송 모두 압축 상태 → consumer 가 압축 해제.
4가지 옵션 비교
| 알고리즘 | 압축률 | 압축 속도 | 압축 해제 속도 | CPU |
|---|---|---|---|---|
| none | 1.0× | 무한 | 무한 | 0 |
| gzip | 3~5× | 느림 | 보통 | 높음 |
| snappy | 2~3× | 빠름 | 빠름 | 낮음 |
| lz4 | 2~3× | 매우 빠름 | 매우 빠름 | 낮음 |
| zstd | 3~4× | 빠름 | 빠름 | 중간 |
선택 가이드
CPU 여유가 있고 네트워크가 비싼 환경이면 gzip / zstd (압축률 우선). CPU 가 부족하고 네트워크는 충분하면 snappy / lz4 (속도 우선). 균형을 원하면 zstd (최신, 가장 균형). 소량·테스트면 none (압축 없음).
기본 권장은 zstd (Kafka 2.1+). 가장 균형 잡혔어요.
효과 — 텍스트·JSON 데이터
JSON 같은 반복적 텍스트 는 5~10배 압축 까지 가요. 디스크·네트워크 비용 대폭 절감.
바이너리·이미지는 압축률 1.0~1.5×. 효과가 미미해요.
여기서 정말 중요한 자리 — 이미 압축된 데이터 (이미지·동영상·미리 압축된 파일) 는 압축 효과 없음. CPU 만 낭비합니다. application level 에서 미리 압축 하든지, 아니면 압축 disable.
End-to-End 압축 vs Per-Message
여기서 시험 함정 — Kafka 는 message 한 개씩 압축 X, MessageSet 전체 압축.
Per-message 압축은 각 메시지마다 별도 압축 헤더 가 붙고, 짧은 메시지 는 압축 후 오히려 더 커지는 경우가 생겨요. Per-MessageSet 압축은 수십~수백 메시지를 통째로 압축 하니 효율이 매우 높고, 반복 패턴 도 전부 활용해요.
이게 Kafka 압축이 효과적인 이유 입니다.
CPU vs 네트워크 — 트레이드오프
| 시나리오 | 압축 알고리즘 |
|---|---|
| 같은 머신 (loopback) | none (CPU 절약) |
| 같은 데이터센터 (10 Gbps) | snappy / lz4 |
| 다른 데이터센터 (1 Gbps) | zstd |
| 인터넷·VPN (제한된 대역폭) | gzip / zstd |
대부분 환경에서는 zstd 또는 lz4 가 답이에요. snappy 는 historical reasons (예전부터 기본값이라 호환성 이유) 로 자주 보입니다.
Producer-Side 추가 효율
Async Send
producer.send(record, (metadata, exception) -> {
if (exception != null) {
// 실패 처리
}
});
비동기 send 가 동기 send (.send().get()) 보다 훨씬 빠릅니다. 내부 buffer + batch 가 자동으로 도와줘요.
Direct ByteBuffer
Kafka client 가 off-heap ByteBuffer (JVM heap 바깥의 메모리 버퍼) 를 써서 JVM heap 압박 X.
Lingering for Larger Batches
linger.ms=20
20ms 대기 후 batch 전송 — 지연 vs 처리량 트레이드오프.
Consumer-Side 효율
Fetch Min Bytes
fetch.min.bytes=1024
fetch.max.wait.ms=500
최소 1KB 또는 500ms 후 batch 받음 → 큰 batch 처리 효율.
Max Poll Records
max.poll.records=500
한 번에 최대 500개 받음 → batch 처리.
한계·실무 함정
1. 이미 압축된 데이터에 압축
위에서 강조. CPU 만 낭비. 비활성.
2. linger.ms 너무 큼 = 지연 폭증
linger 100ms + batch 못 채워서 100ms 풀 대기 = 최대 100ms 지연. 실시간 시스템에서는 낮은 linger (5~10ms).
3. TLS + zero-copy 충돌
위에서 강조. 대규모 + TLS = kTLS 또는 TLS proxy.
4. 압축 ratio 모니터링
효과 없으면 비활성. broker 메트릭 MessagesInPerSec·BytesInPerSec 비율로 추정.
5. zstd 클라이언트 지원
오래된 클라이언트 = zstd 미지원. 도입 전 모든 producer/consumer 버전 확인.
시험 직전 한 번 더 — Kafka Efficiency 함정 압축 노트
- Zero-Copy =
sendfile()시스템 콜로 user space 거치지 않음 - 전통 = 4번 copy + 2번 context switch
- Zero-copy = 1번 copy (DMA only)
- Java =
FileChannel.transferTo() - 효과 = 처리량 60~70% 향상, consumer 많을수록 큼
- 함정 — TLS 활성 시 zero-copy 효과 감소 (user space 암호화)
- 해결 = kTLS (kernel TLS) 또는 TLS proxy
- Batching = MessageSet 단위로 수십~수백 메시지 묶음
- 처리량 10~100배 향상
- Producer 설정 =
linger.ms+batch.size+compression.type linger.ms= 지연 vs 처리량 trade-off- 같은 MessageSet 이 write·read·network 모두 통과 — 재batch 없음
- Compression 4가지 = gzip · snappy · lz4 · zstd
- gzip = 압축률 ↑ CPU ↑
- snappy = 속도 ↑ CPU ↓
- lz4 = 매우 빠름
- zstd = 가장 균형 (Kafka 2.1+ 권장)
- 기본 권장 = zstd
- 효과 — JSON 5~10배 / 바이너리·이미지 1.0~1.5배
- 이미 압축된 데이터 = 압축 X (CPU 낭비)
- Per-MessageSet 압축 (Per-message 가 아닌) — 반복 패턴 전부 활용
- 환경별 선택 — 같은 머신=none / 같은 DC=snappy/lz4 / 다른 DC=zstd / 인터넷=gzip/zstd
- Producer 추가 효율 — async send, off-heap ByteBuffer, linger
- Consumer 추가 효율 — fetch.min.bytes, max.poll.records
- 함정 — linger 너무 큼 = 지연 폭증
- 함정 — 이미 압축된 데이터에 압축
- 함정 — TLS + zero-copy 충돌
- 함정 — zstd 클라이언트 호환성 확인
공식 문서: Kafka Design — Efficiency 에서 자세한 사양을 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 80편 — Apache Kafka란 + 이벤트 스트리밍의 표준
- 81편 — Kafka 활용 영역 7가지 (메시징·활동 추적·로그·이벤트 소싱)
- 82편 — Kafka Quickstart (5분 hands-on · topic·producer·consumer)
- 83편 — Kafka Design: Motivation (왜 이렇게 설계됐나)
- 84편 — Kafka Design: Persistence (디스크가 빠르다)
다음 글:
- 86편 — Kafka Design: Producer (Partition 선택·ACK·Idempotent)
- 87편 — Kafka Design: Consumer (Pull · Consumer Group · Offset)
- 88편 — Kafka Message Delivery Semantics (at-most·at-least·exactly-once)
- 89편 — Kafka Replication (ISR · Leader Election · Unclean)
- 90편 — Kafka API 5종 종합 (Producer · Consumer · Streams · Connect · Admin)