카프카 데이터의 기본 단위가 메시지고, 같은 토픽의 같은 파티션으로 가는 메시지들을 묶은 것이 배치다.
바이트 배열인 메시지
메시지는 DB의 row나 record와 같은 위치에 있고, 특별한 의미 없는 단순 바이트 배열이다. 카프카는 안에 무엇이 들었는지 모르고 알 필요도 없다. 형식과 의미는 스키마의 몫이고 그것도 카프카 밖에 있다.
배치가 만드는 맞바꿈
메시지 하나마다 네트워크 I/O를 하면 오버헤드가 막대하다. 그래서 효율성을 위해 배치 단위로 묶어서 저장하고, 여기서 카프카 전체를 관통하는 트레이드오프가 나온다.
배치 크기 ↑ → 시간당 처리량 ↑
→ 각 메시지의 전달 지연 ↑
처리량과 지연은 같이 좋아지지 않는다. 배치를 키우면 한 번에 많이 보내니 처리량은 오르지만, 배치가 찰 때까지 기다리므로 개별 메시지는 늦게 도착한다.
linger.ms와 batch.size
조절하는 다이얼은 linger.ms와 batch.size다. 앞은 배치를 보내기 전에 배치가 찰 때까지 기다릴 시간이고 뒤는 배치 크기다. 프로듀서가 계속 덜 찬 배치를 보내고 있다면 linger.ms를 올려 처리량을 얻고, 꽉 찬 배치가 나가는데 메모리에 여유가 있으면 batch.size를 올린다.
message.max.bytes
브로커가 받을 수 있는 최대 크기는 message.max.bytes가 정하고 기본값은 1MB다. 여기서 두 가지를 놓치기 쉽다.
기준이 압축된 크기다. 프로듀서는 압축 결과가 이 값보다 작기만 하면 압축 전 기준으로 훨씬 큰 메시지도 보낼 수 있다.
컨슈머 쪽 설정과 맞아야 한다. 컨슈머가 한 번의 요청으로 받을 수 있는 양이 브로커가 받아주는 메시지보다 작으면, 컨슈머는 큰 메시지를 읽는 데 실패하고 거기서 읽기가 멈춘다. 브로커 사이의 replica.fetch.max.bytes도 마찬가지다.
큰 메시지의 대가
크기를 키우면 네트워크 연결 시간, 요청을 처리하는 브로커 스레드의 요청당 작업 시간, 디스크에 써야 하는 크기가 모두 늘어난다. 큰 데이터는 개체 저장소(blob store)나 계층화된 저장소에 넣고 카프카에는 참조만 보내는 쪽이 낫다.
참고
원본은 컨슈머 쪽에서 맞춰야 할 설정으로 fetch.message.max.bytes를 적었는데 이것은 구 Scala 컨슈머의 설정이고 지금의 자바 컨슈머에는 없다. 대응하는 것은 요청 전체의 상한인 fetch.max.bytes(기본 52428800)와 파티션별 상한인 max.partition.fetch.bytes(기본 1048576)다. 다만 둘 다 하드 리밋이 아니라서, 첫 번째 파티션의 첫 배치가 이 값보다 크면 진행이 막히지 않도록 그 배치는 그대로 리턴된다. Kafka Consumer Configs