Amazon MSK Express 브로커는 Kafka 클러스터 운영의 복잡성을 크게 줄입니다. 스토리지를 별도로 프로비저닝하거나 용량을 예측할 필요가 없고, 복제 계수(RF=3)와 최소 ISR(minISR=2)이 고정되어 있어 잘못된 복제 설정으로 인한 문제를 원천적으로 방지할 수 있습니다. KRaft 모드로 생성하면(Kafka 3.9 이상) ZooKeeper 관리 부담도 사라집니다.
사이징도 단순해집니다. 인스턴스 타입별 처리량 기준선 (sustained ingress/egress)이 문서에 숫자로 공개되어 있어, 필요한 처리량을 해당 숫자로 나누면 브로커 대수가 나옵니다. Kafka 트랜잭션을 사용하지 않는 워크로드라면 이 계산은 실제로 성립합니다.
그런데 Kafka 트랜잭션을 사용하는 순간, 이 단순한 공식은 적용되지 않습니다. express.m7g.2xlarge 3대 클러스터(sustained ingress 187.5 MB/s)에서 측정한 결과, 트랜잭션 한 건에 10개 레코드를 담아 커밋하는 워크로드는 120.5 MB/s, 네트워크 기준선의 64%에서 처리량이 멈췄습니다. 그 지점에서 브로커 CPU는 44%, 클라이언트 CPU는 5%였고, 브로커 네트워크 스로틀링도 발생하지 않았습니다. 프로듀서를 48개에서 96개로 두 배 늘려도 처리량은 123.8 → 120.5 MB/s로 늘지 않았습니다. 더 작은 인스턴스에서 레코드를 5개씩 묶는 극단적인 경우에는 sustained 기준선의 18%(일반 전송 실측 대비 12%)밖에 쓰지 못했습니다.
이 글에서는 Kafka 트랜잭션이 브로커에 추가하는 작업을 단계별로 살펴보고, 트랜잭션 워크로드의 처리량 상한을 계산하는 식을 세운 뒤, 그 식의 어느 항이 실제로 워크로드를 제약하는지 판별하는 방법을 설명합니다. 이어서 프로듀서·컨슈머·클러스터 각 계층에서 어떤 순서로 무엇을 조정해야 하는지, 그리고 그 조정을 검증하기 위한 측정 방법을 다룹니다.
이 글의 모든 수치는 특정 조건에서 측정한 값이며 메시지 크기, 파티션 수, 트랜잭션 단위, 클라이언트 구성이 다르면 값은 달라질 수 있습니다. 이 블로그의 목적은 테스트 결과 보다 병목이 어디서 발생하는지를 판별하고 상황에 맞게 최적화 하는 방법을 소개하는데 있습니다.
Kafka 트랜잭션의 보장 범위, 그리고 Express 브로커 특성
트랜잭션은 두 가지를 보장합니다
기본 설정의 Kafka 프로듀서는 at-least-once를 보장합니다. 네트워크 오류로 응답을 받지 못해 재시도하면 같은 레코드가 두 번 기록될 수 있습니다. enable.idempotence=true (Kafka 3.0 이상 기본값) 설정은 프로듀서 세션 안에서 재시도로 인한 중복을 막아 주지만, 트랜잭션은 그보다 더 큰 범위의 두 가지를 보장합니다.
여러 파티션·여러 토픽에 걸친 원자적 쓰기. 예를 들어, 주문 이벤트를 orders 와 order-audit 두 토픽에 쓸 때, 둘 다 저장되거나 둘 다 저장되지 않습니다.
read-process-write (exactly-once semantics) 패턴. 컨슈머 오프셋 커밋을 프로듀서 트랜잭션에 포함시켜( sendOffsetsToTransaction ), 읽고 처리해서 쓰는 파이프라인을 정확히 한 번 수행합니다. Kafka Streams의 processing.guarantee=exactly_once_v2 가 내부적으로 사용하는 방식입니다.
이 보장을 위해 Kafka는 다음 요소를 사용합니다.
요소
역할
transactional.id
프로듀서의 논리적 ID. 재시작 후 이전 세션의 미완료 트랜잭션을 정리(fencing)하는 기준입니다.
Producer epoch
같은 transactional.id 의 새 인스턴스가 등장하면 epoch


