RocksDB State Store Provider — 대용량 스트리밍 상태의 GC 압박 해결
Spark Dynamic Partition Pruning 및 RocksDB State Store Provider 공식 문서 종합 (웹 리서치, 2026) — Apache Spark 공식 문서(structured-streaming-programming-guide.html) + Apache Spark JIRA(SPARK-11150) + 커뮤니티 기술 블로그 Apache Spark 공식 문서 Structured Streaming Programming Guide — State Store
이 모듈을 다 읽으면
- 기본 HDFSBackedStateStore가 왜 수백만 개 키 규모에서 GC 압박을 유발하는지 설명할 수 있다
- RocksDB State Store가 이 문제를 해결하는 메커니즘(네이티브 메모리+로컬 디스크)을 설명할 수 있다
spark-learn-37에서 다룬 '스트리밍은 체크포인트 위치에 상태 저장소를 둔다'는 개념을 전제로, 그 상태 저장소의 기본 구현(HDFSBackedStateStore)이 대용량 상태에서 겪는 GC 문제와 이를 해결하는 RocksDB 기반 구현체를 정리한다.
기본 구현의 한계 — 상태가 전부 JVM 힙 안에 있다
streaming aggregation, dropDuplicates, stream-stream join, mapGroupsWithState/flatMapGroupsWithState 같은 상태 기반(stateful) 연산에서 수백만 개의 키를 유지해야 하는 경우, 기본 구현인 HDFSBackedStateStore는 이 상태 데이터를 전부 executor의 JVM 메모리 안에 객체로 유지한다. 상태 객체 수가 많아지면 JVM 힙에 메모리 압박이 커지고, 이는 큰 GC(가비지 컬렉션) 일시정지로 이어져 마이크로배치 처리 시간이 들쭉날쭉해지는(high variation) 문제를 유발한다 — 이는 spark-learn-37에서 다룬 '스트리밍 상태는 오래 유지된다'는 개념이 실제로 부딪히는 구체적인 운영상의 대가다.
핵심 포인트
- HDFSBackedStateStore(기본)는 상태 데이터를 executor의 JVM 힙 메모리에 객체로 유지
- 상태 객체가 많아지면(수백만 키 규모) JVM GC 압박이 커져 마이크로배치 처리 시간이 들쭉날쭉해짐
RocksDB 구현체 — 네이티브 메모리+로컬 디스크로 JVM 힙 압박을 회피
Spark 3.2부터 제공되는 RocksDBStateStoreProvider는 상태 데이터를 JVM 힙이 아니라 네이티브 메모리와 로컬 디스크에 저장해 GC 압박 자체를 피해간다. spark.sql.streaming.stateStore.providerClass를 org.apache.spark.sql.execution.streaming.state.RocksDBStateStoreProvider로 설정하면 활성화되며, 체크포인트 위치에 변경 사항이 그대로 저장돼 기본 구현과 동일한 장애 허용성을 유지한다 — 즉 '어디에 상태를 두느냐'만 바뀔 뿐 장애 복구 시 상태를 복원하는 보장 자체는 달라지지 않는다.
핵심 포인트
- RocksDBStateStoreProvider(Spark 3.2+)는 상태를 네이티브 메모리+로컬 디스크에 저장해 JVM GC 압박을 회피
- spark.sql.streaming.stateStore.providerClass 설정으로 활성화, 체크포인트 기반 장애 허용성은 기본 구현과 동일하게 유지
RocksDB 자체의 메모리도 무한정은 아니다 — boundedMemoryUsage
RocksDB로 옮긴다고 메모리 문제가 완전히 사라지는 건 아니다 — 노드 하나에 여러 RocksDB 인스턴스가 동시에 돌면(파티션마다 인스턴스가 생김) 이들의 메모리 사용량 합이 제한 없이 계속 늘어나 결국 OOM을 유발할 수 있다. spark.sql.streaming.stateStore.rocksdb.boundedMemoryUsage를 true로 설정하면 한 노드에서 실행되는 모든 RocksDB 인스턴스의 총 메모리 사용량에 상한(spark.sql.streaming.stateStore.rocksdb.maxMemoryUsageMB, 기본 500MB)을 걸 수 있다 — write buffer와 block cache가 이 상한 안에서 나눠 쓰는 비율도 별도 설정(writeBufferCacheRatio, highPriorityPoolRatio)으로 조정 가능하다.
핵심 포인트
- RocksDB도 노드당 여러 인스턴스가 동시에 돌면 메모리 사용량이 무제한 증가해 OOM 유발 가능
- boundedMemoryUsage=true + maxMemoryUsageMB로 노드당 RocksDB 총 메모리 상한을 걸 수 있음(기본 500MB)