6. Kafka 파티션 이용해서 성능 향상 시키기 (+실습)

2025. 12. 1. 16:42·개발 공부/kafka

서론

  • 지난 시간에 Consumer가 메시지를 한 번에 하나씩만 처리하는 현상으로 인해 비효율적으로 메시지를 처리하는 모습을 볼 수 있었다.
    • 이를 해결하기 위해 kafka의 파티션을 알아야 한다.
  • 본 실습을 하기 전 EC2에 kafka가 설치 되어야하며 Producer와 Consumer가 구축된 로컬 스프링 서버가 필요하다. 만약 구축이 안되어 있다면 아래 링크를 참고하자

[환경 세팅]

 

2. Kafka 간단히 설치해보기 (feat. EC2) (+실습)

환경 세팅 전 갖추어야할 것AWS 계정적당한 성능의 EC2이번 실습은 AWS 프리티어에서 진행하기 위해 EC2 t2.micro (메모리 1GB)에서 진행함EC2에 Kafka 설치 / 실행하기1. JDK 17 설치하기Kafka를 실행시키려

student-developer-story.tistory.com

 

4. Kafka의 Producer, Consumer 서버 만들기 (feat. Spring) (+실습)

사전 준비이번에는 Kafka의 메시지를 주는 Producer와 받는 Consumer 서버를 SpringBoot로 만들어 볼 것이다.이에 앞서 AWS EC2에 Kafka가 설치되어 있어야 한다.만약 필요하다면 밑의 링크를 참고하면 됩니

student-developer-story.tistory.com

 

5. Kafka 메시지 처리 실패 시 대처 방법 (feat. Spring) (DLT, 재시도) (+실습)

서론Kafka를 이용하여 비동기 통신을 할 경우 사용자에게는 성공 여부와 상관없이 바로 성공이라고 보내지기 때문에 실패할 시 서버에서 처리를 해주어야한다.이를 위해 재시도, DLT 등의 방식이

student-developer-story.tistory.com

 

파티션(Partition)이란?

  • 파티션은 큐를 여러 개로 늘려서 병렬 처리가 가능하게 하는 기본 단위

5가지 특징

1. 각 토픽은 하나 이상의 파티션을 만들 수 있다.

2. Producer가 특정 토픽에 메시지를 넣으면 파티션에 알아서 분산되어 들어간다.

 

3. 하나의 파티션은 하나의 consumer에게만 할당된다.

아래 처럼은 불가능 하다.

4. 하나의 컨슈머는 여러 개의 파티션을 처리할 수 있다.

  • 여러 개의 컨슈머가 하나의 파티션을 처리하는 것은 불가능하지만 하나의 컨슈머가 여러 개의 파티션을 처리하는 것은 가능하다.

5. 하나의 파티션에 할당된 하나의 컨슈머는 메시지를 순서대로 처리한다.

  • 오프셋 순서를 보장하기 위해서 하나의 컨슈머가 하나의 파티션을 처리할 때는 앞선 메시지가 처리된 후에 처리가 된다. (멀티 쓰레드라고 해서 병렬 처리가 되는 것이 아님)
    • ex) 컨슈머 0이 파티션 0의 첫번째 메시지를 처리한 다음에 1 처리 가능

 

특정 토픽의 파티션 수 조회하기 / 설정하기 / 변경하기

  • 기존처럼 파티션 수를 하나로 해서는 병렬처리가 불가능하다. (파티션 수를 늘려주어야 한다.)

특정 토픽의 파티션 수 조회하기

bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--describe --topic email.send

  • PartitionCount는 1, Partition은 0인 것을 볼 수 있다.
    • PartitionCount : 토픽이 가지고 있는 파티션의 총 개수
      • 별도의 옵션을 주지 않고 토픽을 생성하면 기본적으로 파티션은 1개만 생성된다.
    • Partition : 파티션 번호 (0번 부터 시작)

토픽 생성할 때 파티션 수 설정하기

1. 토픽 생성하기 (+ 파티션 수 설정하기)

# 문법
bin/kafka-topics.sh \
	--bootstrap-server <kafka 주소> \
	--create \
	--topic <토픽명> \
	--partitions <파티션 수>
	
# 예제
bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--create \
	--topic test.topic \
	--partitions 3

2. 잘 생성됐는 지 확인하기

bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--describe --topic test.topic

  • partitonsCount가 3, partition이 실제로 3개가 나온 모습을 볼수 있음

기존 토픽의 파티션 수 늘리기

1. 기존 토픽의 파티션 수 늘리기

# 문법
bin/kafka-topics.sh \
	--bootstrap-server <kafka 주소> \
	--alter \
	--topic <토픽명> \
	--partitions <변경할 최종 파티션 수>
	
# 예제
bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--alter \
	--topic test.topic \
	--partitions 5

2. 잘 생성됐는 지 확인하기

bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--describe --topic test.topic

  • 총 5개가 잘 생성된 모습을 볼 수 있음

기존 토픽의 파티션 수 줄이기

bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--alter \
	--topic test.topic \
	--partitions 3

  • 파티션 수를 증가는 가능하지만 줄일 수는 없음
    • 처음에 신중히 결정해야 함
  • 줄이기 위해서는 새로운 토픽을 만들고 기존의 토픽을 마이그레이션을 해주어야함.

 

파티션이 여러 개일 때 여러 개의 컨슈머로 메시지가 골고루 들어가는 지 확인하기

파티션에 어떻게 메시지가 들어가는 지 설명

1. key가 포함되지 않은 메시지를 넣을 경우
스티키 파티셔닝(배치 단위로 처리하기 위해 하나의 파티션에 메시지가 일정량이 채워져야만 그 다음 파티션에 메시지를 저장) 방식으로 메시지를 분배한다.

Kafka 2.4 Ver 이전에는 라운드 로빈 방식(번갈아가면서 차례대로 하나씩 배분하는 방식)으로 메시지를 분배했었다. 하지만 대규모 메시지를 처리할 때는 스티키 파티셔닝 방식이 성능적으로 효율적이었기 때문에 Kafka 2.4 Ver 이후로는 기본 메시지 분배 방식이 라운드 로빈 방식에서 스티키 파티셔닝 방식으로 변경됐다.

2. key가 포함된 메시지를 넣을 경우
key의 해시 값을 기반으로 파티션을 결정해서 메시지를 분배한다. 그래서 같은 key 값을 가진 메시지는 같은 파티션에 들어간다.

실습

1. 기존 토픽 삭제 후 다시 생성

# 토픽 삭제
bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--delete --topic email.send

# 토픽 생성 (+ 파티션 3개 생성)
bin/kafka-topics.sh \
	--bootstrap-server localhost:9092 \
	--create \
	--topic email.send \
	--partitions 3

2. 실시간으로 토픽의 파티션에 저장되는 메시지 확인할 수 있게 셋팅하기

# email.send 토픽의 모든 메시지를 조회 (+ 파티션 정보도 같이 출력)
bin/kafka-console-consumer.sh \
	--bootstrap-server localhost:9092 \
	--topic email.send \
	--from-beginning \
	--property print.partition=true

3. Producer 서버 실행시켜서 API 요청으로 메시지 넣어보기

  • 스티키 방식이기에 같은 파티션으로 조회되는 모습을 볼 수 있음
  • 스티키 파티셔닝 방식이 대규모의 데이터를 처리할 때는 유리하지만, 작은 규모의 데이터를 처리할 때는 하나의 파티션에만 메시지가 몰리기 때문에 비효율적이다. (라운드 로빈 방식과 스티키 파티셔닝 방식을 잘 생각하고 쓸 줄 알아야한다.)

라운드 로빈 방식으로 설정 변경하는 방법

  • Producer 역할을 하는 Spring Boot 서버의 application.yml 파일을 아래와 같이 수정하면 된다.

application.yml

spring:
  kafka:
    bootstrap-servers: 15.164.96.71:9092
	    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      properties:
	      partitioner.class: org.apache.kafka.clients.producer.RoundRobinPartitioner

 

여러 개의 컨슈머로 메시지 병렬적으로 처리하기

  • 파티션을 여러 개로 해보았으니 이제 여러 개의 컨슈머로 병렬적으로 처리해보자

1. 프로듀서, 컨슈머 서버 실행시키기

  • 컨슈머 서버의 경우 복사를 해서 2개를 띄우자

2. 컨슈머 서버 로그 확인하기

consumer-0
consumer-1

  • 각기 다른 파티션이 할당된 모습을 볼 수 있다.

3. 메시지 보내보기

(좌) consumer-0, (우) consumer-1

  • 메시지를 병렬적으로 처리하는 모습을 볼 수 있다.

그림으로 이해하기

  • 파티션이 하나 였을 때는 컨슈머 서버가 여러 대이더라도 소용이 없었다. 왜냐하면 하나의 파티션은 하나의 컨슈머 서버에서만 담당해서 처리할 수 밖에 없기 때문이다. 그래서 아래 그림과 같이 파티션을 늘렸다.

  • 서버를 늘린 결과 더 빠르게 메시지를 처리할 수 있었지만 서버가 부족하다면 서버를 늘리는 것이 맞겠지만 부족하지 않은 상황에서 서버를 늘리는 것은 비효율적일 수 밖에 없다. → 한 서버에서 병렬적으로 처리하는 방법 필요하다

 

하나의 컨슈머로 메시지 병렬적으로 처리하기

1. 하나의 컨슈머 서버만 실행시키기
이전 실습으로 인해 컨슈머 서버를 두 대 띄워뒀다면 한 대를 종료시키자.

2. 컨슈머 서버의 코드 수정하기

EmailSendConsumer

@KafkaListener(
          topics = "email.send",
          groupId = "email-send-group", // 컨슈머 그룹 이름
          concurrency = "3" // 여기를 추가 시켜 주어야 함(멀티 쓰레드를 활용해 병렬적으로 처리할 파티션의 개수)
)
//    @RetryableTopic(
//            attempts = "5", // 시도 횟수
//            backoff = @Backoff(delay = 1000, multiplier = 2), // 재시도 간격 (1초, 2배씩 늘려나감) 1초.. 2초.. 4초..
//            dltTopicSuffix = ".dlt"
//    )
public void consume(String message) {
    System.out.println("Kafka로부터 받아온 메시지: " + message);

    EmailSendMessage emailSendMessage = EmailSendMessage.fromJson(message);

    if (emailSendMessage.getTo().equals("fail@naver.com")) {
        System.out.println("잘못된 이메일 주소");
        throw new RuntimeException("잘못된 이메일 주소");
    }

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    System.out.println("이메일 발송 완료");
}

3. 컨슈머 서버 재실행시키고 API 요청 보내면서 컨슈머 서버 로그 확인하기

  • API 요청을 연속으로 3번 보낸 뒤에 컨슈머 서버의 로그를 확인해보자. 로그를 보면 3개의 메시지를 병렬적으로 처리하는 걸 확인할 수 있다.

위와 같이 쓰레드를 늘리면 성능을 올릴 수 있다. 하지만 파티션 수를 무작정 늘리면 오히려 비효율적이다. (쓰지 않는 파티션을 늘린다면 그걸 유지하는 비용이 들기 때문이다.)

 

적정 파티션 개수 계산하는 방법

  • 적정 파티션 개수를 정할 때의 "핵심은 처리가 지연되는 메시지가 생기지 않는 선에서 파티션을 최소로 설정하는 것"이다. 공식으로 표현하자면 다음 공식을 만족하게 파티션 수를 결정하면 된다.
    • 프로듀서가 보내는 메시지량 ≤ 하나의 쓰레드가 처리하는 메시지량 x 파티션 수
    • 간단하게 생각하면 Producer에서 보내는 것만큼 Consumer에서 처리하면 된다는 것이다.

가상으로 적정 파티션 수 계산 해보기

1. 몇 개의 쓰레드를 사용해야 처리량이 가장 높아지는 지 측정하기
Spring Boot 서버는 멀티 쓰레드 기반이기 때문에 동시에 여러 요청을 처리할 수 있다. 이 때, 몇 개의 쓰레드를 사용해야 요청을 가장 많이 처리할 수 있는 지 측정해야 한다.
→ 100개의 쓰레드를 활용하는 게 가장 효율적이라고 측정했다고 가정하자.

2. 하나의 컨슈머 서버가 처리할 수 있는 최대 처리량(Throughput)을 알아내기
컨슈머 서버가 적절한 쓰레드 개수를 기반으로 요청을 처리한다고 했을 때, 최대 처리량(Throughput)이 얼마나 되는 지 측정해야 한다.
→ 하나의 컨슈머 서버(100개의 쓰레드를 활용)가 1초에 처리할 수 있는 처리량(Throughput)이 30이라는 걸 알아냈다고 가정하자. 즉, 1개의 쓰레드가 1초당 0.3개의 요청을 처리한다는 뜻이다.

3. 프로듀서가 보내는 평균 메시지량 알아내기
프로듀서가 보내는 메시지라 함은 사용자가 API 요청을 얼마나 보내는 지와 같은 의미이다. 사용자가 1초당 평균적으로 얼마나 요청을 보내는 지를 측정하거나 예상해야 한다.
→ 사용자가 평균적으로 1초당 100개의 메일을 보낸다고 가정하자.

4. 처리가 지연되지 않는 선에서 파티션 개수 계산하기
처리가 지연되지 않으려면 프로듀서에서 들어오는 메시지의 수보다 더 빨리 처리할 수 있어야 한다. 그리고 평균 메시지량이 어느 정도 초과할 것도 예상해서 계산해야 한다.
→ 평균 메시지량이 어느 정도 초과할 것을 예상해서 1초당 120개 정도를 처리할 수 있게 만드려면, 아래 공식에 의해 적정 파티션 수는 400개라는 걸 알 수 있다.

공식 적용 : 프로듀서가 보내는 메시지량(120) ≤ 하나의 쓰레드가 처리하는 메시지량(0.3) x 파티션 수(400)

 

컨슈머가 메시지를 지연 없이 잘 처리하고 있는 지 확인하는 방법 (Consumer Lag)

Lag 이란?

  • 카프카에서 사용하는 랙(Lag)의 의미는, 지연된 메시지 수(컨슈머가 아직 처리하지 못한 메시지 수)이다. 다른 말로, 컨슈머 랙(Consumer Lag)이라고도 부른다.
    • 우리가 평소에 컴퓨터에 랙 걸린다를 생각하면 이해하기 쉽다.

컨슈머 랙(Consumer Lag)은 언제 발생할까?

  • 프로듀서의 메시지 생산량보다 컨슈머의 메시지 처리량이 작을 때 컨슈머 랙(Consumer Lag)이 발생한다.
    • 생산되는 메시지 양이 3개인데, 처리되는 양이 1개라면 발생하는 것이다.
    • 현업에서는 갑작스럽게 요청이 증가할 때나 컨슈머에 장애가 생겼을 때 주로 발생한다.
    • 컨슈머 랙이 시간이 갈수록 계속해서 늘어나고 있다면 빠르게 조치를 취해야 한다. 그렇지 않으면 사용자들 입장에서 요청을 보낸 내용이 처리되지 않아 서비스에 버그가 생겼다고 생각할 것이다.

컨슈머 랙(Consumer Lag) 확인하는 방법

  • 우선 컨슈머 랙(Consumer Lag)을 확인하기 위해 컨슈머 랙(Consumer Lag)을 의도적으로 만들자.

1. 프로듀서 서버만 켜두고, 컨슈머 서버 종료하기

2. 요청 보내기

간단하게 postman으로 Producer 서버에 요청을 보내면 된다.

3. CLI로 컨슈머 랙 확인하기

# 컨슈머 그룹 세부 정보 조회하기
bin/kafka-consumer-groups.sh \
	--bootstrap-server localhost:9092 \
	--group email-send-group \
	--describe

  • LAG은 처리되지 않은 메시지를 뜻하는 데, 각 파티션 마다 있는 것을 확인할 수 있다.
  • CLI는 직접 확인해야하기 때문에 계속 지속적으로 확인하기 힘들다

 

현업에서의 컨슈머 랙(Consumer Lag) 체크 방법

1. 외부 모니터링 툴 사용하기

  • Datadog (유료)
  • Burrow (무료)
  • Prometheus, Grafana (무료)

2. 매니지드 서비스(Managed Service)에서 제공하는 모니터링 기능 사용하기

  • 현업에서는 카프카를 직접 구축해서 사용하지 않고, 클라우드의 카프카 서비스를 사용하는 경우도 많다. 대표적인 서비스로 AWS MSK와 Confluent Cloud가 있다. 이 서비스를 사용하면 자체적으로 컨슈머 랙(Consumer Lag)에 대한 모니터링 기능을 같이 제공하는 경우가 많다.

 

출처: https://inf.run/qzJua

저작자표시 (새창열림)

'개발 공부 > kafka' 카테고리의 다른 글

Kafka 고가용성 확보하기 (node, broker, controller, cluster, replication) (+실습)  (0) 2025.12.05
5. Kafka 메시지 처리 실패 시 대처 방법 (feat. Spring) (DLT, 재시도) (+실습)  (0) 2025.11.24
4. Kafka의 Producer, Consumer 서버 만들기 (feat. Spring) (+실습)  (0) 2025.11.23
3-2. Kafka 토픽 네이밍 규칙  (0) 2025.11.21
3-1. Kafka의 핵심 요소와 작동 (Topic, Consumer, Producer, Consumer Group, OffSet, CURRENT OFFSET) (+ 실습)  (0) 2025.11.21
'개발 공부/kafka' 카테고리의 다른 글
  • Kafka 고가용성 확보하기 (node, broker, controller, cluster, replication) (+실습)
  • 5. Kafka 메시지 처리 실패 시 대처 방법 (feat. Spring) (DLT, 재시도) (+실습)
  • 4. Kafka의 Producer, Consumer 서버 만들기 (feat. Spring) (+실습)
  • 3-2. Kafka 토픽 네이밍 규칙
Jamey
Jamey
  • Jamey
    컴공 대학생의 이야기
    Jamey
  • 전체
    오늘
    어제
    • 분류 전체보기 (36)
      • 개발 공부 (33)
        • k8s (24)
        • kafka (8)
        • AI (1)
      • 개발기 (2)
      • 프로젝트 홍보 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    current offset
    consumer
    Producer
    AI
    serialDB
    Kubernetes
    K8S
    Rag
    Kafka
    sql자동화
    topic
    Linux
    토픽
    Jenkins
    조인 쿼리
    cloudflare workers
    Graphana
    llm최적화
    langchain
    카프카
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Jamey
6. Kafka 파티션 이용해서 성능 향상 시키기 (+실습)
상단으로

티스토리툴바