카프카 파티션 개수 산정식 설계 여정

Broker 특성, Latency, Peak traffic, Backfill catch-up을 모두 고려한 과정

Pepper • Hi, im pepper! im Software engineer🙂

  • Backend

카프카 파티션 개수 설정 전략

Kafka 파티션 개수는 토픽의 처리량, 컨슈머 병렬성, 순서 보장에 직접 영향을 주는 설계값입니다.

채널에서는 토픽을 만들 때 사용할 수 있는 파티션 개수 계산 기준을 정리했습니다. 목표는 모든 상황에 맞는 정답을 찾는 것이 아니라, 토픽을 만드는 사람이 같은 기준으로 적정한 초기 파티션 수를 판단할 수 있게 만드는 것입니다.

1. 먼저 보는 최종 산정식

토픽을 만들 때 개발자가 준비해야 하는 값은 세 가지입니다.

  1. 프로듀서 평균 처리량(MB/s)

  2. 프로듀서 피크 처리량(MB/s)

  3. 컨슈머가 lag을 따라잡아야 하는 시간 기준

채널에서는 파티션 수를 계산할 때 아래 기준도 함께 적용합니다.

  • egress_per_partition = 0.1MB/s

    • 채널의 파티션 산정식에서 사용하는 고정 기준값입니다.

    • 순차 처리 모델에서 record 1개를 50ms 이내로 처리하는 것을 전제로 합니다.

    • 이 값을 바꾸려면 별도 실측과 검증이 필요합니다. 컨슈머 처리 모델뿐 아니라 Kafka 플랫폼의 처리량 한도와 파티션 한도에 따라 안전하게 쓸 수 있는 값이 달라지기 때문입니다.

  • 1 + consumer_down_time / catchup_time = 5

    • 채널에서 권장하는 기본 catch-up 배수입니다.

    • 토픽 특성에 따라 조정할 수 있지만, 5보다 커지면 Confluent Cloud Kafka 리소스를 비효율적으로 사용하게 될 수 있습니다.

계산식은 아래와 같습니다.

Plaintext
required_partitions = ceil(max(
  producer_peak_throughput / ingress_per_partition,
  consumer_requirement / egress_per_partition
))

consumer_requirement = max(
  producer_avg_throughput * (1 + consumer_down_time / catchup_time),
  producer_peak_throughput
)

아래에서는 이 식을 어떻게 도출했는지, 그리고 필요한 기준값을 채널에서 어떻게 측정했는지 설명합니다.

2. 용어

  • 파티션: 토픽을 나눈 단위입니다. Kafka에서 파티션은 쓰기, 읽기, 컨슈머 병렬 처리의 기본 단위가 됩니다.

  • 레코드: Kafka에 쓰고 읽는 개별 메시지입니다. 같은 key를 가진 레코드는 같은 파티션으로 들어가며, 같은 파티션 안에서는 순서가 보장됩니다.

  • ingress: 프로듀서가 토픽에 쓰는 처리량입니다.

  • egress: 컨슈머가 토픽에서 읽어 처리하는 처리량입니다.

  • lag: 프로듀서가 쌓은 레코드를 컨슈머가 아직 처리하지 못해 밀린 양입니다.

3. 왜 파티션 수를 신중히 정해야 할까?

파티션 개수는 토픽의 처리량과 컨슈머 병렬성을 결정합니다. 파티션이 부족하면 프로듀서가 충분히 빠르게 쓰지 못하거나, 컨슈머가 lag을 따라잡지 못합니다.

그렇다고 파티션이 많을수록 항상 좋은 것도 아닙니다. 파티션 수가 늘어나면 브로커가 관리해야 하는 메타데이터, 파일 핸들, 리더 선출, 복구 비용도 함께 늘어납니다. 장애가 발생했을 때 회복 시간이 길어질 수도 있습니다. 따라서 파티션 개수는 “많을수록 안전하다”가 아니라 “필요한 만큼, 약간의 여유를 두고” 정하는 것이 좋습니다.

순서 보장도 중요한 고려 사항입니다. Kafka는 같은 파티션 안에서만 순서를 보장합니다. key 기반으로 같은 key의 레코드를 같은 파티션에 보내는 경우, 파티션 개수가 바뀌면 key가 매핑되는 파티션도 달라질 수 있습니다. 이 때문에 순서가 중요한 토픽은 생성 이후 파티션을 늘리기 어렵습니다.

4. 파티션 산정식 도출

Confluent 블로그에서는 처리량을 기준으로 파티션 수를 정할 때, 목표 처리량 t와 파티션 하나가 처리할 수 있는 produce 처리량 p, consume 처리량 c를 두고 최소 파티션 수를 max(t/p, t/c)로 설명합니다. 이는 요구량이 처리 능력을 넘지 않아야 한다는 점에서 리틀의 법칙과 같은 관점으로 볼 수 있습니다.

우리가 사용한 산정식도 같은 구조에서 출발했습니다. 다만 운영에서 producer와 consumer가 감당해야 하는 요구량은 서로 다르게 해석해야 했습니다. 그래서 먼저 요구량과 처리 능력을 아래처럼 나누어 보았습니다.

Plaintext
required_partitions = ceil(max(
  producer_requirement / ingress_per_partition,
  consumer_requirement / egress_per_partition
))

파티션 수를 정한다는 것은 결국 토픽이 처리해야 하는 요구량을 파티션 하나가 안정적으로 처리할 수 있는 양으로 나누는 문제입니다. 이 식을 계산하려면 producer_requirementconsumer_requirement를 먼저 정의해야 합니다.

1) producer_requirement 계산

Producer 관점의 요구량은 비교적 단순합니다. 토픽은 피크 시점에도 producer가 쓰는 데이터를 받아낼 수 있어야 하므로 producer_requirementproducer_peak_throughput으로 보았습니다.

Plaintext
producer_requirement = producer_peak_throughput

2) consumer_requirement 계산

Consumer 관점은 조금 더 복잡합니다. Consumer는 평소에 새로 들어오는 데이터를 따라가는 것뿐 아니라, 장애나 배포로 멈춘 뒤 쌓인 lag도 일정 시간 안에 따라잡아야 합니다. 그래서 consumer_requirement는 두 가지 중 큰 값으로 잡았습니다.

  1. 평소 평균 유입량을 기준으로 lag을 따라잡는 데 필요한 처리량

  2. 피크 유입량을 실시간으로 따라가기 위한 처리량

Plaintext
consumer_requirement = max(
  producer_avg_throughput * (1 + consumer_down_time / catchup_time),
  producer_peak_throughput
)

여기서 평균 처리량을 사용하는 이유는 downtime 동안 쌓이는 lag이 순간 피크가 아니라 일정 기간 누적된 데이터이기 때문입니다. 대신 피크 트래픽도 놓치지 않기 위해 producer_peak_throughput을 별도로 비교했습니다.

여기서 (1 + consumer_down_time / catchup_time)은 catch-up 시간 동안 필요한 처리량 배수를 의미합니다. 컨슈머가 멈춰 있는 동안에는 producer_avg_throughput * consumer_down_time만큼 lag이 쌓입니다. 그리고 컨슈머가 복구된 뒤 catch-up을 하는 동안에도 프로듀서는 계속 새 데이터를 씁니다.

그림으로 보면 아래와 같습니다.

catch-up 시간 동안 컨슈머가 처리해야 하는 데이터는 두 가지입니다.

  1. catch-up 시간 동안 새로 들어오는 데이터: producer_avg_throughput * catchup_time

  2. 다운 시간 동안 이미 쌓인 lag: producer_avg_throughput * consumer_down_time

따라서 catch-up 시간 동안 필요한 평균 처리량은 아래처럼 계산됩니다.

Plaintext
consumer_requirement_for_catchup
= producer_avg_throughput * (catchup_time + consumer_down_time) / catchup_time
= producer_avg_throughput * (1 + consumer_down_time / catchup_time)

여기에 피크 트래픽도 놓치면 안 됩니다. 그래서 평균 기반 catch-up 요구량과 피크 처리량 중 더 큰 값을 consumer_requirement로 사용합니다.

기본값은 아래처럼 둘 수 있습니다. 왜 이 배수를 기본값으로 두었는지는 뒤의 egress_per_partition 측정 결과와 Confluent Cloud의 파티션 한도를 연결해 다시 설명하겠습니다.

Plaintext
consumer_down_time = 4일
catchup_time = 1일
1 + consumer_down_time / catchup_time = 5

이 경우 consumer_requirement는 평균 처리량의 5배가 됩니다. 컨슈머가 4일 동안 멈췄을 때, 복구 후 1일 안에 밀린 데이터를 따라잡을 수 있는 수준입니다.

더 빠르게 복구해야 한다면 비율을 키우면 됩니다.

Plaintext
consumer_down_time = 7일
catchup_time = 1일

consumer_requirement = producer_avg_throughput * 8

이 경우 컨슈머는 7일치 lag을 1일 안에 따라잡을 수 있어야 합니다.

3) 최종 파티션 수 계산

이제 파티션 수는 producer와 consumer 양쪽 조건을 모두 만족해야 합니다.

Plaintext
producer_requirement <= ingress_per_partition * partitions
consumer_requirement <= egress_per_partition * partitions

두 조건을 파티션 수 기준으로 바꾸면 아래와 같습니다.

Plaintext
partitions >= producer_requirement / ingress_per_partition
partitions >= consumer_requirement / egress_per_partition

두 조건을 모두 만족해야 하므로 최종 파티션 수는 둘 중 더 큰 값의 올림이 됩니다.

Plaintext
required_partitions = ceil(max(
  producer_peak_throughput / ingress_per_partition,
  consumer_requirement / egress_per_partition
))

계산 결과는 반드시 올림 처리해야 합니다. 예를 들어 계산 결과가 6.2라면 파티션은 최소 7개가 필요합니다.

여기까지 오면 남는 질문은 하나입니다. ingress_per_partitionegress_per_partition은 어떤 값으로 둬야 할까요? 평균 처리량, 피크 처리량, catch-up 목표는 토픽의 비즈니스 요구사항에 가깝습니다. 반면 ingress_per_partitionegress_per_partition은 Kafka를 어디에서 운영하는지, 메시지 크기, producer/consumer 설정, 네트워크, 브로커 사양, 컨슈머 처리 시간에 따라 달라집니다. 그래서 공개 문서의 권장값은 출발점으로만 사용하고, 실제 운영 기준은 우리가 사용할 환경에서 직접 측정하기로 했습니다.

5. 파티션당 처리량 기준 정하기

1) 공개 가이드는 출발점일 뿐

ingress_per_partition은 파티션 하나가 안정적으로 받을 수 있는 쓰기 처리량입니다.

공개 가이드를 보면 플랫폼마다 권장값이 다릅니다.

  • Confluent Cloud는 계획용 권장치로 Basic/Standard 기준 파티션당 ingress 약 5MB/s, Enterprise 기준 약 6MB/s를 제시합니다.

  • Alibaba Cloud ApsaraMQ for Kafka 문서는 파티션당 1-5MB/s를 권장하며, 저지연 워크로드는 낮은 쪽을 사용하라고 안내합니다.

  • Grafana Mimir의 Kafka backend 문서는 처리량 기준보다는, 자동 생성되는 토픽의 파티션 수가 최소한 한 zone의 ingester 수 이상이어야 한다는 조건을 안내합니다. 따라서 Mimir 기준을 일반 Kafka 토픽의 MB/s 기준으로 바로 가져오기는 어렵습니다.

채널에서는 이런 공개 가이드를 그대로 고정값으로 사용하기보다, Kafka 운영 환경별 기본값을 따로 두는 방향이 더 안전하다고 보았습니다. 우리가 사용하는 Confluent Cloud Enterprise 환경에서는 공식 가이드의 6MB/s를 실측 전 계획 단계의 보수적인 출발점으로 볼 수 있습니다. 이후 운영 환경을 대상으로 측정한 값이 있다면, 실측값과 공식 계획값 중 더 작은 값을 기준으로 다시 계산하는 것이 안전합니다.

2) egress는 컨슈머 처리 시간이 지배한다

egress_per_partition은 단순히 Kafka가 읽어줄 수 있는 최대 속도가 아닙니다. 실제로는 컨슈머 애플리케이션이 레코드 하나를 처리하는 시간이 더 큰 영향을 주는 경우가 많습니다.

따라서 egress_per_partition은 아래 두 값 중 작은 값으로 보는 것이 안전합니다.

Plaintext
egress_per_partition = min(
  kafka_platform_egress_per_partition,
  consumer_processing_capacity_per_partition
)

Kafka 플랫폼이 파티션당 15MB/s 읽기를 지원하더라도, 컨슈머 로직이 레코드 하나를 처리하는 데 오래 걸리면 실제 egress_per_partition은 훨씬 낮아집니다.

간단한 계산 예시는 아래와 같습니다.

Plaintext
batch_records = 64
record_size = 1KB
worker_threads = 16
processing_time_per_record = 25ms

total_batch_size = 64KB
processing_waves = 64 / 16 = 4
batch_processing_time = 4 * 25ms = 100ms

consumer_processing_capacity = 64KB / 100ms
                             = 약 0.64MB/s

이 계산은 컨슈머 처리 시간이 파티션당 처리량으로 어떻게 바뀌는지 보여주기 위한 단순 예시입니다. 이론상으로는 약 0.64MB/s지만, 운영에서는 여유를 두고 0.5MB/s처럼 낮춰 잡을 수 있습니다. 이 예시는 16개 worker를 사용하는 병렬 처리 모델이고, 뒤에서 다루는 실측값은 순차 처리 모델 기준입니다.

위 예시의 25ms를 기준으로 보면, 레코드 처리 시간이 50ms가 될 때 처리량은 약 0.32MB/s로 절반이 됩니다. 100ms가 되면 약 0.16MB/s로 50ms 대비 다시 절반, 25ms 대비 1⁄4 수준입니다. 처리 시간이 길어지면 egress_per_partition이 낮아지고, 같은 consumer_requirement를 처리하기 위해 필요한 파티션 수도 그만큼 늘어납니다. 그래서 레코드당 처리 시간을 어떤 값으로 볼지 적절히 결정하는 것이 중요합니다. 특히 외부 API 호출, DB 쓰기, 복잡한 비즈니스 로직이 포함된 컨슈머는 Kafka 자체보다 애플리케이션 처리 시간이 병목이 됩니다. 개발자는 이 값을 직접 입력하기보다, 토픽에 적용된 egress_per_partition 기준을 만족할 수 있도록 컨슈머 처리 시간을 일정 수준 이하로 유지해야 합니다.

6. Confluent Cloud에서 직접 측정하기

공식 문서의 권장값만으로는 충분하지 않습니다. Kafka 운영 플랫폼, 클러스터 설정, 메시지 크기, producer/consumer 설정, 컨슈머 처리 시간이 모두 다르기 때문입니다. 그래서 채널에서는 Confluent Cloud Enterprise eCKU 환경에서 ingress_per_partitionegress_per_partition을 직접 측정했습니다.

측정의 목적은 단순히 최대 처리량을 찾는 것이 아닙니다. 운영에서 지속 가능하게 사용할 수 있는 보수적인 기준값을 찾는 것입니다.

1) 측정값의 안정성 판단: CV

처리량 측정에서는 최대값만 보지 않았습니다. 같은 평균 처리량이라도 윈도우마다 값이 크게 흔들리면 운영 기준으로 쓰기 어렵기 때문입니다. 우리가 찾는 값은 순간적으로 한 번 나온 최고점이 아니라, 운영 중 지속적으로 기대할 수 있는 처리량입니다.

평균은 “대체로 얼마를 처리했는가”를 보여주고, 흩어진 정도는 “1분 윈도우마다 처리량이 얼마나 출렁였는가”를 보여줍니다. CV(coefficient of variation)는 이 흩어진 정도를 평균으로 나눈 값입니다.

Plaintext
CV = 처리량의 흔들림(표준편차) / 평균 처리량

CV가 낮으면 처리량이 평균 주변에 안정적으로 모여 있다는 뜻이고, CV가 높으면 평균은 비슷해도 순간적으로 크게 흔들린다는 뜻입니다. 예를 들어 평균 처리량이 100MB/s일 때 CV가 0.15라면, 윈도우별 처리량의 흔들림이 평균의 약 15% 수준이라는 의미로 볼 수 있습니다. 채널에서는 그림의 왼쪽처럼 1분 윈도우 기준 CV가 0.15 이하이고, retry, throttle, rebalance, error가 없는 구간만 안정적인 결과로 보았습니다.

2) ingress_per_partition 측정

ingress_per_partition은 producer가 파티션 하나에 안정적으로 쓸 수 있는 처리량입니다. 채널에서는 운영 후보 파티션 수 P별로 총 throughput을 단계적으로 올리면서, 처리량과 produce request latency의 관계를 보았습니다.

Plaintext
sustainable ingress_per_partition
= maximum sustainable total throughput(P) / P * safety factor

여기서 maximum sustainable total throughput(P)는 latency가 급격히 튀기 직전, 즉 knee point 직전의 총 처리량입니다. 운영 변동성, 멀티 파티션 오버헤드, 일반적인 skew를 고려하기 위해 safety factor는 0.75를 적용했습니다.

운영 경계는 “시스템이 완전히 망가지는 지점”이 아니라, 아래 조건 중 하나가 처음 나타나는 지점으로 보았습니다.

  • 낮은 throughput 구간 대비 latency가 2배 이상 증가

  • retry 발생

  • throttle 발생

  • record-error-rate > 0

  • UnderReplicatedPartitions > 0

실측 결과는 아래와 같았습니다.

P

Throughput

CV

Sustainable/Partition

1

25.9 MB/s

0.007

19.41 MB/s

2

51.7 MB/s

0.021

19.40 MB/s

4

105.8 MB/s

0.005

19.84 MB/s

8

209.1 MB/s

0.003

19.60 MB/s

16

416.0 MB/s

0.004

19.50 MB/s

파티션 수를 1, 2, 4, 8, 16으로 늘려도 파티션당 처리량 저하는 거의 없었습니다. 실측 결과만 보면 파티션당 약 19.4MB/s까지 안정적으로 처리할 수 있었습니다. 하지만 파티션 산정식에 넣는 운영 기준값은 ingress_per_partition = 6MB/s로 결정했습니다. 실측값이 공식 계획값보다 높더라도, 산정 기준은 실측값과 Confluent Cloud Enterprise가 제시하는 파티션당 ingress 계획값 중 더 작은 값을 취하는 편이 안전하기 때문입니다. 다만 이 값은 Confluent Cloud Enterprise eCKU 기준이므로, 다른 Kafka 운영 환경에서는 다시 측정해야 합니다.

3) egress_per_partition 측정

egress_per_partition은 Kafka가 읽어줄 수 있는 속도만으로 결정되지 않습니다. 컨슈머가 레코드 하나를 처리하는 시간이 직접적인 병목이 됩니다. 그래서 채널에서는 비즈니스 로직 처리 시간 상한을 L로 두고, L별로 파티션 하나가 안정적으로 처리할 수 있는 egress를 측정했습니다.

Plaintext
partitions >= egress_requirement_per_group / sustainable egress_per_partition(L)

테스트에서는 실제 비즈니스 로직 대신 sleep(L)로 처리 시간을 시뮬레이션했습니다. 신규 ingress는 넣지 않고, 충분한 backlog를 미리 쌓아둔 뒤 backlog-only replay 방식으로 측정했습니다. 측정 중 backlog가 먼저 고갈되면 실패로 보았고, 1분 윈도우 기준 bytes_consumed_rate의 CV가 0.15 이하인지 확인했습니다. rebalance, poll timeout, commit stall, retry, error도 없어야 안정적인 결과로 보았습니다.

L-sweep 결과는 아래와 같았습니다. 실제로는 P=8, 16, 32, 64 조건에서 모두 측정했고, 파티션 수가 결과에 큰 영향을 주지 않는 것을 확인했습니다. 그래서 본문에는 대표 예시로 P=8, G=4 조건의 결과만 실었습니다. Per-Partition = Total / G / P로 계산했습니다.

L(ms)

Total

Per-Partition

CV

Status

0

818.8 MB/s

25.59 MB/s

0.035

STABLE

1

132.1 MB/s

4.13 MB/s

0.004

STABLE

2

72.0 MB/s

2.25 MB/s

0.001

STABLE

4

36.8 MB/s

1.15 MB/s

0.012

STABLE

6

25.2 MB/s

0.79 MB/s

0.021

STABLE

8

19.1 MB/s

0.60 MB/s

0.001

STABLE

10

15.4 MB/s

0.48 MB/s

0.001

STABLE

20

7.7 MB/s

0.24 MB/s

0.002

STABLE

30

5.2 MB/s

0.16 MB/s

0.004

STABLE

40

3.9 MB/s

0.12 MB/s

0.001

STABLE

50

3.1 MB/s

0.10 MB/s

0.002

STABLE

이 결과에서 중요한 점은 L이 조금만 커져도 파티션당 처리량이 빠르게 낮아진다는 것입니다. 예를 들어 순차 처리 모델에서 레코드 처리 시간이 50ms라면 egress_per_partition은 약 0.1MB/s 수준까지 내려갑니다. 반대로 컨슈머 내부에서 병렬 처리를 잘 활용하면 egress_per_partition을 높일 수 있지만, 이 경우에도 실제 병렬 효율을 별도로 측정해야 합니다.

7. 기본값 결정: egress 0.1MB/s와 catch-up 배수 5

기본값을 정할 때는 egress_per_partition만 따로 볼 수 없습니다. 컨슈머가 멈춘 시간을 얼마나 빨리 따라잡을지, 즉 catch-up 배수와 함께 봐야 합니다.

2026년 5월 15일 기준 Confluent Cloud 문서에 따르면, Enterprise eCKU 1개는 아래 한도를 가집니다.

Confluent Cloud Enterprise eCKU 1개 기준

Ingress

60 MB/s

Partitions (pre-replication)

3,000

Ingress 1 MB/s당 파티션 예산

50

출처: Confluent Cloud: eCKU/CKU comparison, 2026년 5월 15일 기준.

이 표를 파티션 산정식에 대입하면, Enterprise eCKU의 ingress 한도까지 사용하더라도 파티션 한도 안에 들어오는 경계를 계산할 수 있습니다.

이 경계를 보는 이유는 파티션 수가 ingress, egress, connection 같은 처리량 지표보다 훨씬 덜 유연하기 때문입니다. 처리량이나 connection은 부하에 따라 eCKU를 늘리거나 줄여 대응할 수 있지만, 이미 만든 토픽의 파티션 수는 자유롭게 늘리거나 줄이기 어렵습니다. 특히 레코드 처리 순서가 중요하면 파티션을 늘리는 것조차 key 라우팅과 처리 순서에 영향을 줄 수 있고, 파티션을 줄이는 것은 일반적인 운영 절차로는 불가능합니다.

따라서 처리량이 아니라 파티션 수 때문에 1eCKU를 넘어서는 순간, 이후 트래픽이 줄어도 그 파티션 수를 수용하기 위해 더 큰 eCKU를 최소 기준으로 유지해야 합니다. 그래서 기본값을 정할 때는 “현재 처리량을 감당하는가”뿐 아니라 “catch-up 요구량까지 고려해도 1eCKU의 파티션 한도 안에 들어오는가”를 함께 보았습니다.

Plaintext
60 * catch_up_multiplier / egress_per_partition <= 3,000
catch_up_multiplier / egress_per_partition <= 50

경계값만 놓고 보면 catch_up_multiplier = 50 * egress_per_partition입니다. 따라서 catch-up 목표를 먼저 정하면, 그 목표를 만족하기 위해 필요한 최소 egress_per_partition을 계산할 수 있습니다. 그리고 앞의 L별 실측표에서 그 값을 만족하는 레코드 처리 시간 L을 찾으면 됩니다.

catch-up 목표를 Tcatchup = 1일로 고정하면 Tdown별 요구값은 아래처럼 볼 수 있습니다.

Consumer down time

Catch-up 배수

필요한 egress_per_partition

실측표에서 맞는 L

7일

8

0.16 MB/s 이상

30ms

4일

5

0.10 MB/s 이상

50ms

7일 동안 컨슈머가 멈춘 상황까지 기본값으로 잡으려면 레코드당 처리 시간을 30ms 수준으로 유지해야 합니다. 반면 4일 중단, 1일 catch-up을 기준으로 두면 순차 처리 모델에서 L=50ms, egress_per_partition = 0.1MB/s와 맞아떨어집니다.

채널에서는 Kafka를 처음 도입하는 단계였기 때문에 per-partition 처리량을 공격적으로 높게 잡지 않았습니다. 그래서 기본값은 egress_per_partition = 0.1MB/s, catch-up 배수 5로 정했습니다. 이 값을 Enterprise eCKU의 최대 ingress에 대입하면 60 * 5 / 0.1 = 3,000이므로, eCKU의 ingress 한도까지 사용하더라도 파티션 한도 안에 들어옵니다. 이 조합에서는 필요한 파티션 수가 아래처럼 단순화됩니다.

Plaintext
partitions = topic_ingress * (1 + 4) / 0.1
           = topic_ingress * 50

즉 평균 ingress가 2MB/s인 토픽은 약 100개의 파티션이 필요합니다. 이 숫자가 커 보이더라도, 계산의 의미는 명확합니다. egress_per_partition이 낮은 컨슈머에서는 파티션 수가 producer throughput보다 consumer catch-up 요구량에 의해 결정됩니다.

파티션 수를 줄이고 싶다면 per-partition 처리량을 높여야 합니다. 이때는 parallel consumer, 컨슈머 내부 병렬 처리, 비동기 I/O 같은 방식을 검토할 수 있습니다. 다만 이 경우에도 실제 병렬 효율을 다시 측정한 뒤 egress_per_partition을 바꿔야 합니다.

이 기준은 Confluent Cloud Enterprise eCKU의 한도에 맞춘 값입니다. Kafka 플랫폼이 달라지면 같은 산정식을 쓰더라도 처리량 한도와 파티션 한도를 다시 대입해야 합니다. 예를 들어 2026년 5월 15일 기준 AWS MSK Express broker는 브로커 크기마다 권장/최대 파티션 수가 다릅니다.

AWS MSK Express broker size

권장 파티션 수/브로커

최대 파티션 수/브로커

express.m7g.large

1,000

1,500

express.m7g.4xlarge

6,000

8,000

express.m7g.16xlarge

20,000

32,000

위 표에는 대표 브로커 크기 3개만 옮겼습니다. 전체 목록은 AWS 문서에서 확인할 수 있습니다. 출처: Amazon MSK quota: Express broker partition quota, 2026년 5월 15일 기준.

마지막으로, 여기서 사용한 egress_per_partition = 0.1MB/s는 safety factor를 추가로 적용하지 않은 실측값입니다. 계획 단계에서는 egress에도 safety factor를 적용하는 방안을 검토했지만, egress_per_partition을 더 낮게 잡는 만큼 필요한 파티션 수가 그대로 늘어납니다. 그래서 채널에서는 0.1MB/s를 그대로 기준값으로 사용하고, 개발자가 컨슈머의 레코드당 처리 시간을 50ms 이하로 유지하는 방식으로 운영 기준을 세웠습니다.

8. 계산 예시

아래는 Confluent Cloud Enterprise의 공식 계획값을 참고해 ingress_per_partition = 6MB/s로 둔 예시입니다.

  • 프로듀서 평균 처리량: 10MB/s

  • 프로듀서 피크 처리량: 30MB/s

  • 컨슈머 중단 허용: 4일

  • catch-up 목표: 1일

  • ingress_per_partition: 6MB/s

  • egress_per_partition: 0.1MB/s

먼저 consumer_requirement를 계산합니다.

Plaintext
consumer_requirement = max(
  10MB/s * (1 + 4일 / 1일),
  30MB/s
)

= max(50MB/s, 30MB/s)
= 50MB/s

그다음 파티션 수를 계산합니다.

Plaintext
required_partitions = ceil(max(
  30MB/s / 6MB/s,
  50MB/s / 0.1MB/s
))

= ceil(max(5, 500))
= 500

이 경우 프로듀서 쓰기 처리량만 보면 5개면 충분합니다. 하지만 컨슈머가 처리할 수 있는 속도까지 고려하면 최소 500개가 필요합니다. 이 예시는 파티션 수가 Kafka 성능보다 컨슈머 처리 시간에 의해 결정될 수 있음을 보여줍니다.

9 .마치며

Kafka 파티션 수는 단순히 “트래픽이 많으니 많이 만든다”로 정하기 어렵습니다. 프로듀서 처리량, 컨슈머 처리 시간, lag catch-up 목표, 순서 보장 여부, 클러스터 운영 비용을 함께 봐야 합니다.

이 글의 기준값은 Confluent Cloud Enterprise eCKU와 채널의 컨슈머 실행 모델에서 얻은 값입니다. Kafka 플랫폼이나 컨슈머 구조가 달라지면 같은 방식으로 다시 측정해야 합니다.

채널에서는 이 기준을 통해 토픽 생성 시점의 판단을 더 일관되게 만들고자 했습니다. 완벽한 공식이라기보다는, 토픽마다 같은 질문을 반복해서 던질 수 있게 해주는 출발점에 가깝습니다.

채널톡은 개발할 때 단순함과 섬세함을 중요하게 생각합니다. 단순해야 이해하기 쉽고 장애가 나기 어렵습니다. 그리고 그 단순함은 다양한 케이스를 아우르는 섬세함 위에 세워져야 합니다. 하나를 만들더라도 제대로 만들고, 그 위에서 새로운 것을 차근차근 구축해 나가고 싶은 개발자라면 채널톡에 지원해 주세요.

10 .참고

We Make a Future Classic Product