피드로 돌아가기
Kafka Ordering in the Real World: How to Scale Without Killing Performance
Dev.toDev.to
Backend

Kafka Ordering in the Real World: How to Scale Without Killing Performance

전자상거래 시스템이 Transactional Outbox + Keyed Partitioning + Sub-Inbox 3단계 아키텍처로 Kafka 순서 보장 시 발생하는 Head-of-Line Blocking 제거

Amit Kamble2026년 3월 24일9advanced

Context

Kafka에서 order_id를 메시지 키로 사용하여 순서 보장을 구현하면, 한 파티션 내 단일 주문이 외부 시스템 지연(fraud check, database timeout)을 겪을 때 같은 파티션의 모든 주문이 블로킹되는 Head-of-Line (HOL) Blocking 현상이 발생한다.

Technical Solution

  • Transactional Outbox 패턴 적용: 비즈니스 로직에서 Kafka를 직접 호출하지 않고, Order 업데이트와 Message 항목을 동일 데이터베이스 트랜잭션 내 OUTBOX 테이블에 저장한 후, 별도 Relay 프로세스가 테이블을 폴링하여 Kafka에 발행 (dual-write 실패 방지 및 At-Least-Once 보장)
  • Keyed Partitioning으로 시퀀스 보장: order_id를 Kafka Key로 사용하여 특정 주문에 대한 모든 이벤트가 물리적으로 동일 파티션에 순차 저장
  • Sub-Inbox 패턴으로 병렬 처리: Kafka Listener가 메시지를 INBOX 테이블에 기록 후 즉시 offset을 acknowledge하고, 별도 워커 스레드들이 SELECT ... FOR UPDATE SKIP LOCKED를 사용하여 INBOX 테이블에서 동시에 서로 다른 주문을 처리
  • Partition 초과 할당 전략: 파티션 수를 현재 필요량(예: 10)보다 6배 많게(60개) 생성하여 향후 3년 성장에 대비하고 해시 키 변경으로 인한 시퀀스 깨짐 방지
  • DLQ 및 재시도 전략: 실패 메시지를 지수 백오프로 재시도하되, retry limit 도달 시 order_id를 database에 "Blocked" 플래그로 표시하여 해당 주문의 후속 이벤트 처리 중단

Key Takeaway

Kafka 파티션의 엄격한 순서 보장에서 벗어나 데이터베이스 기반 Sub-Inbox로 로직을 이동하면, 절대적 시퀀스 무결성을 유지하면서 동시에 N개 워커 스레드로 병렬 처리를 실현할 수 있다.


이벤트 순서 보장이 필수적인 전자상거래 및 금융 서비스에서 Transactional Outbox + Keyed Partitioning + Sub-Inbox 3단계 구조를 Spring Boot로 구현하면, 단일 파티션의 느린 메시지가 다른 주문의 처리를 블로킹하지 않으면서도 주문 생성 → 결제 승인 → 배송 요청의 절대적 순서를 보장할 수 있다.

원문 읽기
Kafka Ordering in the Real World: How to Scale Without Killing Performance | Devpick