[Kafka] 토픽과 파티션 그리고 발표까지!

suhwani·2024년 10월 7일
0
post-thumbnail

발표 자료와 대본은 아래쪽에 첨부하였습니다.

Partition 개수 설정


시스템 성능을 최적화하기 위해서 Partition 개수를 적절히 설정해야 한다.
파티션은 병렬 처리를 가능하게 하는 핵심 요소이다.
따라서 파티션의 개수에 따라 성능이 크게 달라질 수 있다.

Partition, 병렬 처리


Topic은 여러 개의 파티션으로 나뉜다.
파티션은 독립적으로 처리될 수 있고, 여러 컨슈머가 병렬로 데이터를 소비할 수 있다.
따라서 파티션 개수가 늘어나면, 병렬로 처리할 수 있는 범위가 넓어져 데이터 처리량이 증가하게 된다.
물론 컨슈머의 개수도 충분해야 한다.

파티션 개수를 정할 때 고려할 사항


1. 데이터 처리량

시스템이 처리해야 할 데이터의 양이다.
대규모 시스템에서는 하루에도 수만 건의 트래픽이 몰리고, 수십만의 로그 데이터가 쌓일 것이다.
이러한 많은 데이터들을 처리하기 위해서는 파티션을 많이 설정하여 병렬 처리를 극대화해야 한다.

2. 메시지 키 사용 여부

파티션에 메세지를 분배할 때 메세지 키를 기준으로 할당한다.
만약 메세지 키가 동일한 경우, 같은 파티션에 저장된다.
파티션별로 병렬 처리가 일어나기 때문에, 동일한 파티션에 많은 메시지가 몰리지 않도록
메세지 키를 적절하게 설정해주어야 한다.

3. 브로커와 컨슈머의 영향

파티션 개수가 증가하면 이들을 처리해 줄 브로커와 컨슈머도 증가해야 한다.
그렇지 못하다면 메세지가 처리되지 못하고 쌓이게 될 것이다.
따라서 브로커와 컨슈머의 처리량을 고려하여 적절한 파티션 개수를 결정해야 한다.

데이터 처리 속도를 늘리는 방법


1. 컨슈머 성능 개선

컨슈머의 성능을 개선하면 컨슈머가 처리하는 양을 높일 수 있다.
컨슈머의 성능을 개선한다는 것은 데이터 처리 시간을 줄이거나, 데이터 처리 방식을 효율적으로
바꾸는 등 여러 방법이 있다.
하지만 컨슈머와 연결된 다른 시스템의 성능에도 영향을 받기 때문에 한계가 존재한다.

2. 컨슈머 수 증가

컨슈머 수를 증가하는 것은 병렬 처리되는 데이터 양이 증가하기 때문에, 데이터 처리량을 늘리는 확실한 방법이다. 이 때 컨슈머는 파티션에서 데이터를 병렬로 처리하는데, 성능을 향상 시키기 위해서는 컨슈머만큼 파티션이 확보되어야 한다.

컨슈머 랙 ( Consumer Lag )


컨슈머 랙이란?
컨슈머의 처리량이 프로듀서의 데이터 전송량을 따라가지 못할 때 발생하는 것으로,
데이터를 전송하는 양이 소비하는 양보다 많아져서 빠르게 처리되지 못하고 쌓이는 현상을 말한다.
컨슈머 랙이 발생하면 데이터를 올바른 시간에 처리하지 못하고 시간이 지연되는데, 실시간으로 데이터를
처리해야 하는 시스템에서 심각한 문제를 불러올 수 있다.
따라서 컨슈머 랙을 방지하기 위해서는 컨슈머가 소비하는 데이터 양은 프로듀서가 전송하는 데이터 양보다 많아야 한다.

처리 순서 보장


1. Kafka에서 메세지 소비(consume) 순서는 보장하지 못한다.

이 말은 메세지가 여러 파티션으로 나뉘어 병렬로 처리되기 때문에 순서가 유지되지 못한다는 것이다.
즉 같은 토픽의 다른 파티션에 있는 메세지들은 순서대로 처리되지 않을 수 있다.

2. 메세지 처리 순서를 보장한다.

이 말은 메세지 키가 동일한 데이터는 반드시 동일한 파티션에 저장되기 때문에
해당 파티션을 처리하는 컨슈머가 메세지의 순서를 보장하며 데이터를 처리한다
는 의미이다.
즉 같은 메세지 키를 가지고 있는 데이터는 같은 파티션에 저장되어 순서를 보장한다는 의미이다.

하지만 파티션 개수가 변동이 일어난다면, 메시지 키와 파티션 매칭이 달라질 수 있다.
이 때 처리 순서에 혼란이 발생할 수 있는데, 커스텀 파티셔너( Custom Partitioner )를 개발해
기존 메세지 키와 파티션 간 매칭을 유지한다면 순서를 보장할 수 있다.

3. 메시지 처리 순서를 보장하지 않아도 되는 경우

이와 같이 처리 순서를 보장하지 않아도 된다면 파티션을 경우에 따라 유동적으로 늘리는 방법을
사용할 수 있다.
따라서 처음에는 적절한 파티션 개수를 유지하다가 데이터가 몰리는 경우에 파티션을 늘려서
성능을 최적화할 수 있다.

발표 대본


안녕하세요. 발표자 정수환입니다.
여기어때 쿠폰 서비스 기술 블로그 글을 가져왔습니다.

여기어때 선착순 쿠폰 이벤트 개발 과정 살펴보고, 이벤트 기반 아키텍쳐에 대해서 알아보겠습니다. 코드를 살펴보거나, 전문적인 내용보다는 이렇게 했다더라. 요정도만 살펴보는 식으로 편하게 들어주시면 좋겠습니다.

기존 여기어때 쿠폰 서비스는 RDB에서 쿠폰 재고를 확인하는 방식으로, RDB에 의존한 상황이었습니다. 또한 모놀리틱한 시스템 구조였다고 합니다. 만약 이 상태를 유지한다면 RDB에 쿼리를 날리는 과정에서 발생하는 시간적인 문제, 동시성 이슈로 성능적인 문제 뿐 아니라 쿠폰이 한정된 수량을 넘어서 발급되는 등 여러 문제가 발생할 수 있습니다. 또한 한 쪽에서 문제가 발생하면 모놀리틱한 구조이기 때문에 다른 서비스에도 장애가 전파될 수 있습니다.

성능적인 문제, 동시성을 해결하기 위해서 레디스를 도입했다고 합니다.
레디스는 단일 스레드 기반으로 커맨드를 실행하기 때문에 동시성을 해결하기에 편리하고,
set, sorted set 등 다양한 데이터 타입과 커맨드를 제공하기 때문이라고 합니다.

실제로 어떻게 해결했는지를 살펴보겠습니다.
레디스의 INCR 커맨드는 특정 키의 값을 1씩 증가하는 커맨드인데, 시간복잡도가 빅오(O)에 1을 가져서
매우 빠르게, 지급된 쿠폰 수량을 증가시키고, 재고가 초과했을 때는 지급되지 않도록 처리를 할 수 있습니다.
한 명당 한 장만 지급되는 쿠폰에는 Set 자료형을 사용하여 중복된 사용자를 제거할 수 있었습니다.
마지막은 트랜잭션 입니다. 오른쪽 사진에는 수량을 조회하고 지급하는 과정이 나뉘어져있습니다.
하지만 이는 쿠폰을 기존 수량보다 초과해서 지급하는 문제를 발생시키는데, 조회와 지급이 따로 일어나기 때문입니다. 이를 레디스 트랜잭션을 사용하였습니다. 레디스 트랜잭션은 커맨드 묶어서 실행하면서, 순차 처리를 보장하기 때문에 해당 문제를 해결할 수 있었습니다.

이번에는 성능을 어떻게 해결했는지 보겠습니다. 쿠폰을 지급하는 인설트 쿼리가 비지니스 로직 내에
포함되어 있기 때문에 write DB에 부하가 발생합니다. Read DB 같은 경우 Scale-out을 통해서 트래픽을 분산할 수 있지만, Write DB의 경우 Scale-up을 하더라도, 장애 발생 원인이 될 수 있습니다.
이를 해결하기 위해 이벤트 기반 아키텍쳐인 카프카를 사용하였습니다.
토픽은 타임어택쿠폰이슈로 정하였고, 토픽 바디를 Zero-Payload 방식으로 데이터를 전달하여 복잡성을 낮추기도 하였습니다. 해당 토픽은 파티션 3개, 레플리카 3개, 컨슈머 3대로 운영하고 있습니다.
여기서 제로 패이로드 방식은 메세지에 데이터를 넣는 게 아니라, 데이터 식별자를 넣는 방식을 말합니다.

이번에는 이벤트 쿠폰이 아니라 예약 개발 업무에서 쓴 글인데, 이벤트 기반 아키텍쳐를 적용하면서 카프카를 어떻게 썼는지 짧게 알아보겠습니다.
오른쪽에 기존 API를 호출하는 REST 통신으로 동기 통신을 하는 구조에서 카프카를 사용하여
비동기 통신의 발행 구독 모델을 적용하였습니다.
기존 동기 통신 구조의 문제점은 책임과 역할의 분리가 되지 않았다는 점과 서비스 간 장애 전파의 위험성이 있다는 것인데, 카프카를 사용하여 서비스 간 의존도를 낮추면서 이 같은 문제들을 해결했다고 합니다.

이벤트 기반 아키텍쳐를 적용하기 위해서 카프카가 정답인가에 대한 질문이 글 마지막에 나옵니다.
물론 카프카 성능이 뛰어나고, 안정성도 높다고 하지만, 단일 파티션이 아닌 경우 consume 순서가 보장되지 않는다는 점을 생각해야 합니다. 만약 대기열의 개념으로 카프카를 사용하려고 한다면 단일 파티션을 사용하여 순서를 보장할 순 있지만, 그럴 경우 분산 처리가 불가능하여 SQS나 rabbit MQ 같은 싱글 큐를 사용하는 것이 더 좋은 성능을 낼 수 있습니다.

이상입니다. 감사합니다.







profile
Backend-Developer

0개의 댓글