-
Kafka 파티션 키 전략Develop 2025. 9. 5. 13:47
개요
이커머스 시스템에서 Kafka 파티션 키를 어떻게 설계하고 적용했는지에 대해 다룹니다. 감사 로그, 좋아요 집계, 주문 처리 등 각 도메인별로 어떤 파티션 키 전략을 선택했는지와 그 이유를 설명합니다.
파티션 키의 중요성
Kafka에서 파티션 키는 다음과 같은 역할을 합니다:
- 순서 보장: 동일한 키를 가진 메시지는 같은 파티션으로 전송되어 순서가 보장됩니다
- 부하 분산: 키의 해시값으로 파티션을 결정하여 부하를 분산시킵니다
- 스케일링: 파티션 수만큼 컨슈머를 병렬로 실행할 수 있습니다
도메인별 파티션 키 전략
1. 주문 도메인 - OrderEventPublisher
@Component public class OrderEventPublisher { public void publishSuccess(Long orderId, String userId) { OrderSuccessEvent event = OrderSuccessEvent.create(orderId, userId); kafkaTemplate.send(KafkaTopics.ORDER, orderId.toString(), event); } }파티션 키: OrderId
선택 이유:
- 동일한 주문에 대한 모든 이벤트(생성, 결제, 완료, 취소)가 순서대로 처리되어야 함
- 주문 상태 변경의 정합성 보장
- 주문별로 이벤트를 그룹핑하여 추적 및 디버깅 용이
장점:
- 주문 생명주기 이벤트의 순서 보장
- 주문별 장애 격리 가능
- 특정 주문에 문제가 생겨도 다른 주문에 영향 없음
2. 결제 도메인 - PaymentEventPublisher
@Component public class PaymentEventPublisher { public void publishSuccess(Long orderId, String paymentId) { PaymentSuccessEvent event = PaymentSuccessEvent.create(orderId, paymentId); kafkaTemplate.send(KafkaTopics.PAYMENT_SUCCESS, orderId.toString(), event); } }파티션 키: OrderId
선택 이유:
- 결제는 주문과 강하게 연결된 도메인
- 주문-결제 이벤트 간의 순서 보장 필요
- 주문 도메인과 동일한 파티션 키로 일관성 있는 설계
장점:
- 주문과 결제 이벤트가 같은 파티션에서 순서대로 처리
- 결제 실패 시 보상 트랜잭션의 순서 보장
- 주문별 결제 히스토리 추적 용이
3. 좋아요 감사 로그 - LikeEventPublisher
@Component public class LikeEventPublisher { public void publishLike(String userId, Long productId) { // 감사용 이벤트 ProductLikedEvent auditEvent = ProductLikedEvent.create(userId, productId); kafkaTemplate.send(KafkaTopics.PRODUCT_LIKE, productId.toString(), auditEvent); } }파티션 키: productId
선택 이유:
- 상품별로 좋아요 이벤트를 그룹핑
- 동일한 상품에 대한 좋아요/취소 이벤트 순서 보장
- 상품별 감사 로그 추적 및 분석 용이
장점:
- 특정 상품의 좋아요 히스토리 순차 처리
- 상품별 이상 패턴 탐지 가능 (급작스러운 좋아요 증가 등)
- 상품 도메인 이벤트와의 일관성
파티션 키 선택 시 고려사항
1. 데이터 분산 (Data Distribution)
// ❌ 잘못된 예: 카디널리티가 낮은 키 kafkaTemplate.send(topic, event.getStatus(), event); // "SUCCESS", "FAILED" 등 // ✅ 올바른 예: 카디널리티가 높은 키 kafkaTemplate.send(topic, event.getOrderId().toString(), event); // 고유한 주문 ID문제점: 카디널리티가 낮으면 특정 파티션에 데이터가 집중됨
해결책: 고유값이 많은 필드를 키로 선택2. 순서 보장 (Ordering Requirements)
// 주문 상태 변경 이벤트들이 순서대로 처리되어야 하는 경우 kafkaTemplate.send(KafkaTopics.ORDER, orderId.toString(), orderCreatedEvent); kafkaTemplate.send(KafkaTopics.ORDER, orderId.toString(), orderPaidEvent);핵심: 순서가 중요한 이벤트들은 반드시 같은 파티션으로 전송
3. Hot Partition 방지
// ❌ Hot Partition 발생 가능 kafkaTemplate.send(topic, popularProductId, event); // ✅ 시간 기반으로 분산 String partitionKey = popularProductId + ":" + LocalDate.now(); kafkaTemplate.send(topic, partitionKey, event);전략: 시간, 해시 등을 조합하여 부하 분산
실제 구현 시 주의사항
1. 파티션 키 일관성
public class KafkaTopics { public static final String ORDER = "order-events"; public static final String PAYMENT_SUCCESS = "payment-success-events"; public static final String PAYMENT_FAILED = "payment-failed-events"; public static final String PRODUCT_LIKE = "product-like-events"; public static final String LIKE_AGGREGATION = "like-aggregation-events"; }토픽별로 명확한 파티션 키 전략 문서화
2. 배치 처리 고려
@KafkaListener( topics = LIKE_AGGREGATION, groupId = "Metrics", containerFactory = "BATCH_LISTENER" ) public void handleLikeChangedEvent( List<ConsumerRecord<String, ProductLikeAggregationEvent>> records, Acknowledgment acknowledgment ) { // 같은 파티션의 레코드들은 순서가 보장됨 // 배치 내에서 동일한 (productId, date) 조합은 함께 처리됨 }배치 처리 시 파티션 키의 이점 활용
결론
효과적인 파티션 키 전략은 다음을 고려해야 합니다:
- 비즈니스 로직의 순서 보장 요구사항
- 데이터 분산과 부하 균형
- 도메인별 특성과 접근 패턴
- 확장성과 유지보수성
각 도메인의 특성에 맞는 파티션 키 전략을 적용하여:
- 주문/결제: 트랜잭션 순서 보장
- 감사 로그: 상품별 이벤트 추적
- 집계: 시간 기반 병렬 처리
이를 통해 안정적이고 확장 가능한 이벤트 스트리밍 시스템을 구축할 수 있었습니다.