[Rust Wave] Day 4: 스트리밍의 새로운 강자, RisingWave vs Flink
서론: Flink의 “State” 악몽
지난 10년간 실시간 스트리밍 처리(Stream Processing)의 왕좌는 Apache Flink가 차지하고 있었다. 정확한 1회 처리(Exactly-Once Semantics)와 강력한 윈도우(Window) 기능을 제공했지만, 운영자들에게 Flink는 ‘야수’와 같았다.
가장 큰 고통은 상태 관리(State Management)에서 온다. Flink는 각 컴퓨팅 노드의 로컬 디스크(RocksDB)에 수 TB에 달하는 상태를 저장한다. 이로 인해 스케일 아웃(Scale-out)을 하려면 거대한 상태 데이터를 네트워크로 재전송(Rebalancing)해야 했고, 노드가 하나만 죽어도 복구에 수십 분이 걸렸다. 또한 JVM 기반이기에 발생하는 GC(Garbage Collection)로 인한 지연 시간 튀(Latency Spike)는 실시간 시스템의 숙명과도 같았다.
Rust로 작성된 RisingWave는 이 문제를 “클라우드 네이티브 아키텍처”로 해결하겠다고 나섰다.
1. 아키텍처: Shared-Nothing vs Shared-Storage
두 엔진의 결정적 차이는 “상태(State)를 어디에 두는가?”이다.
1.1 Apache Flink: Coupled Architecture
Flink는 컴퓨팅과 상태가 결합(Coupled)되어 있다.
- 로컬 상태: 중간 연산 결과(State)를 TaskManager의 로컬 RocksDB에 저장한다.
- 체크포인트: 장애 복구를 위해 주기적으로 S3로 스냅샷을 올리지만, 이는 백업 용도일 뿐이다.
- 문제점: 컴퓨팅 리소스를 늘리려면 로컬에 있는 데이터를 새 노드로 이동시켜야 하므로 확장이 느리고 무겁다.
1.2 RisingWave: Decoupled Architecture
RisingWave는 컴퓨팅과 스토리지를 완전히 분리(Decoupled)했다.
- Hummock 스토리지 엔진: RisingWave의 자체 스토리지 엔진인 Hummock은 모든 상태 데이터를 S3(Object Storage)에 저장한다. 컴퓨팅 노드는 로컬에 단지 캐시(Cache)만 보유한다.
- 이점: 컴퓨팅 노드는 상태를 소유하지 않는다(Stateless). 따라서 노드가 죽거나 추가될 때 데이터를 옮길 필요가 없다. 단지 S3를 바라보는 포인터만 갱신하면 된다. 이는 초 단위의 스케일링을 가능하게 한다.
2. 쿼리 모델: Stream Processing as a Database
Flink를 사용하려면 Java/Scala API를 익혀야 하고, DataStream 객체를 다루는 복잡한 로직을 짜야 했다. Flink SQL이 존재하지만 여전히 제한적이다.
RisingWave는 스스로를 “스트리밍 데이터베이스”라고 정의한다.
2.1 Materialized View (MView)
RisingWave의 핵심 추상화는 Materialized View다.
- 사용자는 Kafka 소스에서 데이터를 읽어와서 복잡한 조인과 집계를 수행하는 SQL을 작성하고, 이를
CREATE MATERIALIZED VIEW로 정의한다. - RisingWave는 새로운 이벤트가 들어올 때마다 증분 업데이트(Incremental Update) 방식으로 뷰의 결과를 실시간으로 갱신한다.
- 사용자는 이 뷰를 마치 일반 PostgreSQL 테이블처럼
SELECT쿼리로 조회할 수 있다. (PostgreSQL Wire Protocol 호환)
1
2
3
4
5
6
7
8
9
-- RisingWave: 스트림 처리를 View 정의로 끝낸다.
CREATE MATERIALIZED VIEW ad_stats AS
SELECT
ad_id,
count(*) as clicks,
window_start
FROM TUMBLE(ad_impressions, click_time, INTERVAL '1' MINUTE)
GROUP BY ad_id, window_start;
3. Rust의 위력: No JVM, No GC
실시간 처리에서 가장 예측 불가능한 변수는 Java의 Garbage Collection(GC)이다. 힙(Heap) 메모리가 커질수록 “Stop-the-world” 시간이 길어지고, 이는 전체 파이프라인의 랙(Lag)을 유발한다.
RisingWave는 Rust로 작성되었다.
- 안정적인 Latency: 메모리를 수동으로 관리할 필요 없이 Rust의 소유권 모델이 컴파일 타임에 메모리 안전성을 보장한다. 런타임에 GC가 돌지 않으므로 처리 지연 시간이 매우 안정적이다.
- 효율적인 리소스: JVM 오버헤드가 없으므로 동일한 워크로드를 처리할 때 Flink 대비 적은 메모리와 CPU를 사용한다. 이는 클라우드 비용 절감으로 직결된다.
4. 장애 복구와 운영 비용
엔지니어링의 비용은 개발 비용보다 운영 비용이 더 크다.
4.1 Flink의 장애 복구
Flink 클러스터의 노드 하나가 OOM으로 죽었다고 가정하자.
- 전체 잡(Job)이 멈춘다.
- 마지막 체크포인트(S3)에서 데이터를 다시 가져와 모든 노드의 RocksDB를 재구축한다.
- 재처리(Replay)를 수행하여 현재 시점까지 따라잡는다.
- 결과: 수 분에서 수십 분의 다운타임이 발생할 수 있다.
4.2 RisingWave의 장애 복구
RisingWave의 컴퓨팅 노드가 죽었다고 가정하자.
- 쿠버네티스가 새 파드(Pod)를 띄운다.
- 새 노드는 S3에 있는 데이터를 읽기 시작한다 (데이터 이동 없음).
- 결과: 초(Second) 단위로 복구가 완료된다. 이는 스팟 인스턴스(Spot Instance)를 활용하여 인프라 비용을 극단적으로 낮출 수 있음을 의미한다.
5. 요약: 언제 무엇을 쓸까?
| 특징 | Apache Flink | RisingWave |
|---|---|---|
| 언어 | Java / Scala | Rust |
| 상태 저장소 | Local RocksDB (Coupled) | S3 Object Storage (Decoupled) |
| 주 사용 인터페이스 | DataStream API (Code) | SQL (PostgreSQL 호환) |
| 장애 복구 | 느림 (State 재구축 필요) | 빠름 (Stateless Compute) |
| GC 영향 | 있음 (Latency Spike 위험) | 없음 (Stable Latency) |
| 성숙도 | 매우 높음 (대규모 레퍼런스 다수) | 성장 중 (빠르게 기능 추가 중) |
결론적으로, 초대형 IT 기업에서 수천 대의 노드로 극도의 세밀한 제어(Low-level control)가 필요한 경우가 아니라면, 2026년의 새로운 스트리밍 프로젝트에 Flink를 도입하는 것은 재고해 보아야 한다.
RisingWave는 Rust의 성능과 클라우드 네이티브 아키텍처를 결합하여, 스트리밍 처리의 복잡성을 “데이터베이스를 운영하는 수준”으로 낮췄다. 복잡한 Java 코드와 RocksDB 튜닝에서 해방되고 싶다면, Rust가 만든 이 새로운 물결에 올라타야 한다.