어제 운영 환경에서 40만 건의 데이터를 다른 영역에서 마이그레이션한다고 전달받았습니다. 이 데이터가 변경되면, 제가 담당하는 영역의 동일한 테이블을 동기화하기 위해 Kafka 메시지를 통해 테이블 변경 내역을 수신받아, MERGE INTO를 사용해 데이터를 최신화하는 작업을 수행하고 있었습니다.
그런데 발행하는 쪽에서 문제가 생겨 동일한 내용의 메세지인데 변경처리를 동시에 처리하고 발행해서 수정일시 필드값만 1초차이가 나게 바뀌어서 메세지를 받아 처리하는 consumer 쪽에서 메세지를 동시에 처리하게 되면서 DB에서 PK 중복 오류가 발생을 하였습니다.
다행히 건수가 많지 않았고 수정일시 필드의 값만 다르기도 했고 해당 에러가 났다는것은 이미 해당 데이터를 생성하고 동시에 처리하는 과정이기 때문에 동시에 처리된 중복된 메세지중에 하나는 처리가 되었기 때문에 update 또는 insert가 되어있는 상태인것을 확인하였습니다.
그렇다면 원인과 해결 방법이 무엇일까
문제상황
Kafka에서 발행된 메시지를 여러 컨슈머가 동시에 처리하는 상황에서, 각 컨슈머가 동일한 데이터를 데이터베이스에 삽입 또는 병합하는 과정에서 Primary Key 중복 오류가 발생했습니다. 특히 메시지의 내용이 거의 동일하였고 데이터가 없는 상태에서 동시에 merge 쿼리에서 insert 쿼리로 데이터를 삽입하려고 하다가 하나의 메세지에서는 이미 데이터를 insert 한 상태인데 다른쪽에서는 데이터가 없어 insert를 하다가 데이터가 이미 존재해서 발생한 상태
원인 분석
일단 발행된 메세지를 확인했을때 메세지에 키값이 없는것을 확인하였었고,
Kafka의 동작 방식에서 키값이 지정되지 않으면, 메시지는 Round-robin 방식으로 여러 파티션에 분배됩니다. 이는 각 파티션에서 동시에 여러 컨슈머가 동일한 메시지를 처리할 수 있다는 것을 의미합니다. 문제는 바로 여기에서 발생합니다.
해결 방안
해결은 DB의 락을 잡아서 해결 할 수도 있지만 일단 키값이 없어 발생하는 부분과 동일한 메세지를 발행하는 부분을 처리 해주었습니다.
키값을 넣은 이유와 키값 기반한 카프카의 파티셔닝에 대해 설명하겠습니다. (키값은 PK에 맞게 발행하였습니다. ex) "userId_ordSeq")
키값 기반 파티셔닝
Kafka에서 메시지를 보낼 때 고유한 키값을 지정하면, 해당 키값에 대한 메시지는 항상 같은 파티션으로 분배됩니다. 이 경우, 동일한 파티션에 있는 메시지는 순차적으로 처리되므로, 동시에 여러 컨슈머가 같은 메시지를 처리하는 상황을 방지할 수 있습니다.
- 예를 들어, userID를 키값으로 지정하면 해당 사용자의 모든 메시지는 같은 파티션에 들어가므로, PK 중복 문제를 해결할 수 있습니다.
카프카 키와 파티셔닝의 역할
- 키 기반 파티셔닝 : Kafka는 메시지에 키값이 설정된 경우, 해당 키값을 기준으로 파티션을 결정합니다. 동일한 키값을 가진 메세지는 항상 동일한 파티션으로 전송됩니다.
- 파티션 내 메세지 순서 보장: Kafka는 각 파티션 내에서 메세지의 순서를 보장합니다. 즉, 동일한 파티션에 있는 메세지들은 순서대로 처리가 됩니다. 따라서, 키값이 동일한 메세지들은 한 번에 하나씩 처리가 됩니다.
- 컨슈머 그룹 내 병렬 처리: Kafka는 여러 컨슈머를 사용해 병렬 처리가 가능하지만, 동일한 파티션을 여러 컨슈머가 동시에 읽을 수 없습니다. 각 파티션은 한 번에 한 컨슈머에게만 할당합니다. 결과적으로, 동일한 파티션의 메세지는 동일한 컨슈머에 의해 순차적으로 처리됩니다.
그래서 Kafka에 키값 을 지정하면, 동일한 키값을 가진 메세지는 동시에 중복 처리가 되지 않도록 할 수 있습니다. 각 키값에 맞는 파티션에메세지가 순차적으로 처리되기 때문에 MERGE INTO를 하다가 INSERT가 두번 발생해 PK 중복오류가 발생하는 문제를 해결할 수 있게 됩니다.
그럼 위에서 말한 키 기반 파티셔닝이란 뭘까?
키 기반 파티셔닝이란?
키 기반 파티셔닝은 Apache Kafka에서 메시지 전송 시, 특정 키를 기준으로 메시지를 파티션에 분배하는 방식입니다. 키 기반 파티셔닝은 메시지의 순서 보장과 일관성을 유지하도록 합니다.
파티셔닝이란?
Kafka의 파티션(Partition)은 주제(Topic)를 물리적으로 나눈 단위입니다. 각 파티션은 메시지의 저장소 역할을 하며, 여러 파티션으로 나뉘면 Kafka는 더 큰 처리량과 확장성을 제공합니다. 각 파티션은 독립적인 단위로 작동하며, Kafka 브로커에 분산되어 저장됩니다.
키 기반 파티셔닝의 원리
Kafka 메시지는 키(Key)와 값(Value)으로 구성되며, 파티셔닝 전략을 통해 각 메시지가 어떤 파티션으로 들어갈지 결정됩니다. 키 기반 파티셔닝에서는 다음과 같은 흐름으로 메시지가 분배됩니다:
1. 키값지정: 프로듀서가 메세지를 전송할 때, 각 메세지에 Key를 포함할 수 있습니다. 이 Key는 메세지가 특정 파티션으로 고정되도록 합니다.
2. 파티션 결정: Kafka는 키값을 기반으로 파티션을 결정합니다. 일반적으로, 해시 함수를 사용하여 키값을 해싱하고, 그 결과를 사용해 특정 파티션을 선택합니다. 동일한 키는 항상 동일한 파티션으로 매핑되며, 이로 인해 키값에 따라 파티션이 일관되게 유지됩니다.
EX) 3개의 파티션이 있을때, Kafka는 hash(key) % 3 을 통해 파티션을 결정합니다. 동일한 키는 항상 동일한 파티션에 들어갑니다.
3.파티션 내 메시지 순서 보장: 동일한 파티션에 있는 메시지들은 순차적으로 저장되며, consumer가 이를 순차적으로 읽습니다. 따라서 특정 키를 가진 메시지는 파티션에서 순서가 유지되며 처리됩니다.
키 기반 파티셔닝의 장점
- 순서 보장: 동일한 키를 가진 메시지가 항상 동일한 파티션으로 들어가기 때문에, consumer가 해당 파티션을 읽을 때 메시지의 순서를 보장합니다. 예를 들어, 사용자 ID나 주문 ID를 키로 지정하면 해당 사용자나 주문과 관련된 모든 메시지가 동일한 파티션에 저장되며, 메시지 순서가 유지됩니다.
- 일관성: 동일한 키에 대한 메시지가 같은 파티션으로 전송되므로, 메시지 처리의 일관성을 유지할 수 있습니다. 예를 들어, 거래와 같은 중요 이벤트의 경우 이벤트 순서가 중요한데, 이를 보장할 수 있습니다.
- 성능 최적화: 파티션을 사용하면 메시지가 여러 파티션으로 분산되어 병렬로 처리될 수 있습니다. 다만, 키 기반 파티셔닝을 통해 동일한 파티션에 같은 키값의 메시지가 들어가므로, 관련 데이터를 함께 처리하는 데 유리합니다.
파티셔닝 없이 메시지를 전송할 경우
만약 메시지에 키값이 없으면 Kafka는 Round-robin 방식으로 파티션을 무작위로 선택해 메시지를 분배합니다. 이 경우 특정 메시지가 어느 파티션으로 들어갈지 예측할 수 없으며, 동일한 키에 대해 순서가 보장되지 않습니다. 해당 경우에는 여러 Consumer에서 동시에 처리할 수 있습니다.
병렬 처리 가능: 키값이 없을 경우, 여러 파티션으로 메시지가 분산되므로 여러 개의 컨슈머가 동시에 각 파티션의 메시지를 처리할 수 있습니다. 예를 들어, 4개의 파티션이 있고, 4개의 컨슈머가 있는 컨슈머 그룹이 있다면, 각 컨슈머는 서로 다른 파티션의 메시지를 동시에 처리할 수 있습니다.
예시:
- 메시지 A, B, C, D가 순서 없이 4개의 파티션에 분배된다고 가정할 때:
- A → 파티션 1
- B → 파티션 2
- C → 파티션 3
- D → 파티션 4
- 이 경우, 4개의 컨슈머가 각각 파티션 1, 2, 3, 4의 메시지를 동시에 처리할 수 있습니다.
메세지 처리 흐름
1.프로듀서가 메시지를 발행: 메시지에 키가 없으면 Kafka는 이를 여러 파티션으로 랜덤하게 분배합니다.
2.컨슈머 그룹이 메시지를 소비: 각 컨슈머가 자신에게 할당된 파티션의 메시지를 동시에 읽어 처리합니다.
3.병렬 처리: 여러 컨슈머가 서로 다른 파티션의 메시지를 처리하기 때문에, 전체적으로 높은 처리량을 유지할 수 있습니다.
장점
- 확장성: 키가 없을 경우 여러 파티션으로 분산되기 때문에, 시스템이 수평으로 확장할 수 있습니다. 컨슈머를 추가함으로써 더 많은 메시지를 동시에 처리할 수 있습니다.
- 부하 분산: 메시지가 여러 파티션으로 나뉘어 분배되므로, 각 파티션에 걸쳐 부하가 분산됩니다.
단점
- 순서 보장 없음: 키가 없는 메시지는 파티션 간의 순서가 보장되지 않기 때문에, 소비자가 메시지를 처리할 때 순서가 중요하지 않은 경우에 적합합니다.
- 일관성 저하: 특정 키에 대한 메시지의 일관성이 필요할 경우, 키를 사용하는 것이 더 바람직합니다.
결론
Kafka에서 동시에 같은 메시지를 처리하다가 PK 중복 오류가 발생하는 경우, 키값 기반 파티셔닝을 통해 같은 메시지가 순차적으로 처리되도록 할 수 있게 하는 것이 좋다.
물론 이는 각 메세지의 처리를 어떻게 하느냐에 따라서 키값 여부를 결정하고 판단하여 키값이 없이 처리가 되어야하는 경우에는 데이터베이스 락을 사용한 동기화를 통해 중복 처리를방지해야 합니다.
분산 시스템에서 자주 발생할 수 있는 문제 중 하나로, 적절한 설계와 동시성 제어에 대응 할 수 있습니다.
'Kafka' 카테고리의 다른 글
스프링 부트에서 Kafka 로그 관리하기: 불필요한 로그 숨기고 중요한 로그만 남기는 방법(feat. AppInfoParser) (1) | 2024.10.17 |
---|---|
Apache Kafaka의 주요 요소 - Consumer (0) | 2022.07.15 |
Apache Kafaka의 주요 요소 - Producer (0) | 2022.07.15 |