본문 바로가기
1. 개발/1.4. 데이터 분석

실시간 데이터에서 Skew 발생 시 대응

by 엉짱 2026. 2. 8.
반응형

배치 처리에서의 데이터 스큐(Data Skew)는 "어떤 일꾼(Executor) 하나가 유독 늦게 퇴근하네?" 정도의 문제지만, 실시간 스트리밍에서의 스큐는 "특정 파티션에 불이 나서 전체 파이프라인이 멈추는" 재앙에 가깝습니다. 실시간은 데이터가 계속 밀려들기 때문에 한 곳이 막히면 바로 컨슈머 랙(Lag)이 폭발하기 때문이죠. 배치와는 결이 다른, 실시간 환경에서의 '스큐 진압 작전'을 전해드립니다!


🏗️ 1. 실시간 스큐(Skew)의 정체: "특정 파티션의 과부하"

실시간 데이터에서 스큐가 발생하는 가장 흔한 이유는 '키(Key) 설계의 실패'입니다.

  • 예시: 주식 종목별로 파티션을 나눴는데, 오늘따라 특정 테마주 한 종목에 거래량이 90% 몰린다면? 그 종목을 담당하는 파티션과 컨슈머만 비명을 지르며 랙이 쌓이게 됩니다. 나머지 일꾼들은 놀고 있는데 말이죠.

🛠️ 2. 실시간 스큐 대응 전략: 5단계 전술

① 키 살리기(Salting): "데이터에 소금 뿌리기"

배치에서도 쓰이지만 실시간에서는 훨씬 더 정교해야 합니다.

  • 방법: 원래 키인 종목코드 뒤에 무작위 숫자(0~9)를 붙여 종목코드_1, 종목코드_2로 분산시킵니다.
  • 효과: 특정 종목에 몰린 데이터를 여러 파티션으로 강제 분산시킵니다.
  • 주의점: 나중에 합계(Aggregation)를 구할 때 다시 이 소금(Salt)을 제거하고 하나로 합치는 추가 로직이 필요합니다.

② 커스텀 파티셔너(Custom Partitioner) 도입

카프카의 기본 해시(Hash) 알고리즘에만 맡기지 않는 방법입니다.

  • 전략: 거래량이 압도적으로 많은 '대장주'들은 별도의 전용 파티션으로 가도록 로직을 짭니다. 혹은 라운드로빈(Round-robin) 방식을 써서 키와 상관없이 무조건 골고루 뿌려버립니다. (단, 이 경우 메시지 순서 보장은 포기해야 합니다.)

③ 로컬 집계 (Partial Aggregation / Combiner)

컨슈머가 데이터를 카산드라나 Redis 같은 외부 DB로 보내기 전에, 컨슈머 메모리 내에서 먼저 1차 합계를 내는 방식입니다.

  • 효과: 외부 시스템으로 나가는 I/O 부하를 획기적으로 줄여줍니다. 스큐가 발생해도 컨슈머가 '이미 합쳐진 결과'만 보내기 때문에 뒷단(Database)의 병목을 막을 수 있습니다.

④ 컨슈머 그룹의 동적 확장 (Auto-scaling)

실시간 엔진(Spark Streaming, Flink 등)의 리소스를 유동적으로 조절합니다.

  • 방법: 특정 파티션의 랙이 일정 수치를 넘으면, 해당 작업을 처리하는 익스큐터의 CPU/Memory를 즉시 증설합니다.
  • 한계: 파티션 개수보다 컨슈머를 더 늘릴 수는 없으므로, 근본적인 해결책은 아닙니다.

⑤ 윈도우 최적화 (Windowing Strategy)

실시간 집계 시 윈도우 크기를 조절합니다.

  • 전략: 데이터가 몰리는 피크 타임에는 윈도우 크기를 짧게 가져가서 메모리 점유율을 낮추고, 한가한 시간에는 길게 가져가는 방식입니다.

📊 3. 배치(Batch) 스큐 vs 실시간(Streaming) 스큐 비교

비교 항목 배치 처리 (Spark Batch) 실시간 처리 (Kafka/Streaming)
치명도 전체 작업 시간이 길어짐 (지연) 시스템 마비 (Lag 폭발로 인한 장애)
해결 핵심 셔플(Shuffle) 최적화, 살팅(Salting) 파티셔닝 재설계, 로컬 어그리게이션
재시도 실패한 태스크만 다시 돌리면 됨 리밸런싱 유발로 상황이 악화될 수 있음
모니터링 작업 종료 후 로그 분석 실시간 랙(Lag) 모니터링이 유일한 생존줄

💡 4. 실전 시나리오: "대장주의 급등 상황"

지켜보시는 주식 시장에서 갑자기 '상폐 취소' 뉴스가 떠서 특정 종목 거래량이 1,000배 폭증했다고 칩시다.

  1. 현상: 해당 종목 키를 가진 카프카 파티션 3번의 랙이 100만 건을 돌파합니다.
  2. 대응: * 즉시 프로듀서 단에서 해당 종목에 대해 'Salting'을 적용해 파티션 1~10번으로 분산시킵니다.
  • 컨슈머 단에서는 1초 단위로 메모리에서 거래량을 먼저 합산(Window Aggregation)한 뒤 DW에 넣습니다.
  • DW(Athena/Redshift)에서는 나중에 WHERE 종목코드 LIKE 'ABC_%' 로 조회하여 전체 합계를 봅니다.

🛡️ 5. 예방이 최선입니다: "리밸런싱을 조심하세요"

실시간 스큐를 해결하려고 컨슈머를 늘리거나 줄일 때 발생하는 리밸런싱(Rebalancing)은 불난 집에 부채질하는 꼴이 될 수 있습니다.

  • 팁: 리밸런싱 동안 데이터 소비가 멈추면 랙은 더 쌓입니다. 따라서 Static Membership 설정을 통해 불필요한 리밸런싱을 막고, 파티션 개수는 처음부터 충분히(예: 예상 트래픽의 2~3배) 확보해두는 것이 상책입니다.

📊 요약: 스큐 진압 체크리스트

  1. 모니터링: 파티션별 랙(Lag) 편차를 실시간으로 감시하고 있는가?
  2. 키 설계: 특정 키에 데이터가 몰릴 가능성이 있는가? (그렇다면 Salting 준비)
  3. 메모리: 컨슈머 내 로컬 집계가 가능한 구조인가?
  4. 확장성: 파티션 개수가 충분한가?

반응형