Real-Time Stream Processing
이벤트 스트림을 실시간으로 처리하여 저지연 분석과 피처 계산을 수행하는 시스템
핵심 개념
실시간 스트림 처리는 연속적으로 도착하는 이벤트를 처리하여 집계, 변환, 알림 등을 수행하는 패턴이다. Apache Flink, Apache Spark Structured Streaming, Kafka Streams 등이 대표적이며, 2026년에는 스트리밍-배치 통합(unified engine)과 ML 워크로드 특화 엔진이 주요 트렌드다.
Spark Real-Time Mode (4.1)
Apache Spark 4.1은 마이크로배치와 저지연 사이의 트레이드오프를 해소하는 Real-Time Mode를 도입했다:
- 장시간 에포크 + 경계 체크포인팅: 에포크를 길게 유지하면서 경계에서만 체크포인트
- 동시 map-reduce 스테이지: 파이프라인 병렬성 확보
- 논블로킹 연산자: 결과를 버퍼링 없이 연속 방출
- Spark의 리니지 기반 장애 복구를 유지하면서 ETL과 저지연 사기 탐지를 단일 엔진으로 통합
Volga — Rust 기반 ML 피처 엔진
실시간 ML 피처 계산에 특화된 새로운 엔진:
- 기술 스택: Rust + Apache DataFusion(SQL) + Apache Arrow(컬럼나) + SlateDB(상태 저장)
- 3중 실행 모드: Streaming(실시간 업데이트) + Batch(학습 데이터) + Request(서빙)
- 컴퓨트-스토리지 분리: SlateDB가 S3 위에서 동작하여 독립 스케일링
- ML 특화 윈도우 함수: top-k, categorical aggregation, tiling(월~년 단위 장기 윈도우)
- 아키텍처 비교: Flink보다 ML 집계에 특화, Chronon보다 단일 바이너리로 단순, OpenMLDB보다 쓰기 경로 분리가 명확
Dataflow 모델 핵심 요소
- 워터마크: bounded out-of-orderness 가정으로 이벤트 시간 진행을 추적
- 체크포인팅: Chandy-Lamport 스냅샷 알고리즘 기반 분산 체크포인팅
- Exactly-once: Kafka 오프셋 기반 정확한 리플레이 보장
트레이드오프
| 엔진 | 장점 | 단점 |
|---|---|---|
| Spark Real-Time | 기존 Spark 에코시스템 활용, 통합 엔진 | 새 기능이라 성숙도 미검증 |
| Flink | 성숙한 스트리밍, 풍부한 커넥터 | ML 특화 함수 부족, 별도 서빙 레이어 필요 |
| Volga | ML 피처에 특화, 단일 바이너리 | 초기 단계, 에코시스템 부족 |
Zalando의 Flink Table API vs. DataStream API
Table API가 우아하고 선언적이지만 대규모 조인에서 심각한 상태 관리 문제를 일으킬 수 있다:
문제
- Table API 4개 조인 체이닝 시 상태가 “추가”가 아닌 “곱셈”으로 증가
- 235GB 상태 → CPU 고갈, 11분 스냅샷, 크래시 루프, 1시간 SLA 위협
해결: MultiStreamJoinProcessor
- RocksDB에 SKU별 enrichment 상태 1개 인스턴스만 유지하는 단일 KeyedProcessFunction
- SQL 쿼리 최적화 포기 대신 수동 스트림 처리로 중복성 제거
- 결과: 상태 -76%, 스냅샷 시간 -77%, CPU 안정화, AWS 비용 -13%
교훈
- “4개 조인을 체이닝하면 상태가 추가되는 것이 아니라 곱해진다”
- Flink 2.1이 동일 문제를 해결하는 Multi-Way Join Operator 도입으로 결정 사후 검증
트레이드오프
| 엔진 | 장점 | 단점 |
|---|---|---|
| Spark Real-Time | 기존 Spark 에코시스템 활용, 통합 엔진 | 새 기능이라 성숙도 미검증 |
| Flink Table API | 선언적, SQL 친화적 | 복잡 조인에서 상태 기하급수적 증가 위험 |
| Flink DataStream | 상태 세밀 제어 가능 | 수동 구현, 더 많은 코드 필요 |
| Volga | ML 피처에 특화, 단일 바이너리 | 초기 단계, 에코시스템 부족 |
AutoMQ — S3 기반 Kafka 호환 스트리밍
Kafka의 로컬 디스크 복제를 S3 공유 스토리지로 대체:
- 3계층: ElasticLog → S3Stream(WAL, 업로드, 캐싱) → KRaft 기반 메타데이터 컨트롤 플레인
- 무상태 브로커: 장애 시 데이터 복사 없이 epoch fencing만으로 복구
- WAL 배칭: 250ms 또는 8MB 임계값으로 레이턴시-처리량 균형
- 트레이드오프: 복제 스토리지 3x 제거, 단 약간 높은 쓰기 레이턴시와 S3 요청 비용
Apache Pinot — 실시간 Upsert 컴팩션
SegmentRefreshTask로 append-only 스토리지의 버전 누적 문제 해결:
- 높은 obsolete 비율의 세그먼트 선택 → 유효 문서만 추출 → 머지 → 원자적 교체
- 쿼리 무중단: 새 세그먼트가 빈 bitmap으로 즉시 가시화
- RocksDB 메타데이터 스토어로 메모리 10x 감소
- 스토리지 비용 2-10x 절감, 10억+ 프라이머리 키 관리
Kafka KIP-848 — 차세대 컨슈머 리밸런스
기존 Kafka 컨슈머 리밸런스의 근본적 문제를 서버 사이드 조정으로 해결하는 새 프로토콜:
기존 문제
- 두꺼운 클라이언트: 버그 수정에 모든 컨슈머 업데이트 필요
- 글로벌 동기화 장벽: 단일 오동작 멤버가 전체 그룹 리밸런스 방해
- Fake rebalance: 상태 전파 혼동으로 메트릭 해석 어려움
새 아키텍처
- 3중 에포크: Group Epoch(그룹 변경), Assignment Epoch(목표 할당), Member Epoch(개별 수렴)
- 선언적 할당: 점진적 업데이트 대신 목표 상태를 선언, 코디네이터가 수렴
- ConsumerGroupHeartbeat RPC: JoinGroup/SyncGroup/Heartbeat를 단일 RPC로 대체
- 이벤트 루프 코디네이터:
__consumer_offsets파티션별 복제 상태 머신
현황 (Kafka 4.0+)
- 서버 사이드 assignor (range, uniform) GA
- 무중단 롤링 업그레이드 지원 (혼합 프로토콜)
- Kafka Streams는 KIP-1071로 별도 프로토콜
Interval-Aware 캐싱 — Netflix Druid 최적화
롤링 윈도우 대시보드에서 반복 쿼리를 제거하는 캐싱 프록시:
- Druid 쿼리를 1분 단위 버킷으로 분해, 캐시된 부분은 Cassandra에서 서빙
- 지수적 TTL로 시간 경과에 따른 캐시 효율 극대화
- 결과: 82% 부분 캐시 히트율, 33% Druid 쿼리 감소, 66% P90 레이턴시 개선
Flink Materialized Tables
Flink 1.20에서 도입된 Materialized Table은 테이블 정의에 DDL과 DML을 통합하여 라이프사이클을 관리하는 새로운 오브젝트다:
CREATE TABLE/INSERT 대비 이점
- 라이프사이클 관리: 태스크 매니저 재시작 시 자동 복구 — CT/I, CTAS의 INSERT 잡은 소실됨
- 스키마 진화: ALTER MATERIALIZED TABLE으로 쿼리 변경 시 자동 잡 교체 — CT/I는 수동 잡 중지·재시작 필요
- 일시정지/재개: SUSPEND 시 세이브포인트 자동 생성, RESUME으로 상태 유지 재시작
리프레시 모드
| 모드 | 동작 | FRESHNESS 의미 |
|---|---|---|
| CONTINUOUS | 스트리밍 잡으로 실행 | 체크포인트 간격 |
| FULL | 스케줄러 기반 배치 | cron 주기 |
| AUTOMATIC | FRESHNESS 기준 자동 선택 | 30분 미만이면 CONTINUOUS |
파티셔닝과 한계
- 파티션 기반 FULL 리프레시는 현재 파티션만 갱신 — 지연 도착 데이터에 구멍 발생 가능
- 내장 스케줄러(EmbeddedQuartzScheduler)는 메모리 기반으로 프로덕션 부적합
- 카탈로그 지원 제한: 현재 Paimon과 test-filesystem만 완전 지원
Flink Shadow Testing (Grab)
프로덕션 Flink 배포 시 다운타임을 제거하는 병렬 테스팅 기법:
- Shadow 앱: Main 앱과 동일 프로덕션 환경에 별도 K8s 네임스페이스로 배포
- 격리된 커넥터:
isShadow=true환경 변수로 Kafka 컨슈머 그룹·싱크 토픽을 자동 분리 - 배포 흐름: Shadow 배포 → 1시간 안정성 관찰 → 사용자 확인 → Main 배포 → 10분 헬스체크
- 스테이징에서 발견할 수 없는 프로덕션 트래픽 볼륨·체크포인트 호환성 문제를 안전하게 검증
Stream Processing vs Real-Time OLAP
스트림 처리와 실시간 OLAP은 겹치는 용어(“real-time analytics”)로 혼동되지만 근본적으로 다른 계산 경계를 가진다:
| 차원 | Stream Processing | Real-Time OLAP |
|---|---|---|
| 계산 시점 | 데이터 흐름 중 (push) | 쿼리 시점 (pull) |
| 패턴 | 연속적, 상태 기반 | 대화형, 애드혹 |
| 상태 관리 | RocksDB 로컬 상태 | 컬럼나 스토리지 + 인덱스 |
| 출력 | Kafka 토픽, 다운스트림 서비스 | BI 도구, 대시보드 |
핵심 원칙: 예측 가능한 메트릭·자동 액션 → 스트림 처리, 예측 불가능한 탐색·대시보드 → OLAP. Flink를 대시보드 쿼리 엔진으로, OLAP을 연속 ETL로 사용하는 것은 안티패턴이다.
Streaming Database(Flink SQL 포함)는 두 계층을 통합하려 하지만, 고동시성 또는 대규모 상태에서 전용 시스템 대비 한계가 존재한다.
연관 개념
- Change Data Capture
- Spark at Scale
- Feature Store
- Distributed Systems Reliability
- Semi-Structured Data
- Silent Failures and Data Integrity
- Transactional Outbox Pattern
- Multimodal Search
Source: Breaking the Microbatch Barrier, Volga - A Rust Rewrite of a Real-Time ML Engine, Zalando - Why We Ditched Flink Table API Joins - Cutting State by 75%, AutoMQ Shared Storage Architecture Deep Dive, Consistent Scalable Compaction for Real-Time Upserts in Apache Pinot, KIP-848 Next Generation Consumer Rebalance Protocol, Stop Answering the Same Question Twice, Materialized Tables in Apache Flink, Flink Shadow Testing at Grab, Stream Processing vs Real-Time OLAP