Flink State vs 외부 저장소를 활용한 값 관리: 어떤 선택이 더 합리적일까?
개요
실시간 데이터 처리 파이프라인에서 흔히 등장하는 과제 중 하나는 “사용자 피로도, Aggregation등” 값 관리입니다.
예를 들어, 마케팅 알림이나 이벤트 푸시 발송 시스템에서 동일 사용자에게 너무 많은 알림이 전달되지 않도록 시간/횟수 기반 제약을 두어야 할 필요도 있고, 일정 시간내의 이벤트 Aggregation등의 처리가 필요한 경우도 있습니다.
이를 구현하는 방법은 크게 두 가지로 나눌 수 있습니다.
- Flink State를 통한 관리
- Redis 등 외부 저장소를 통한 관리
실제 운영 환경에서 이 두 가지 접근법을 모두 검토했으며, 각 방식의 장단점과 보완 방법을 정리해 보았습니다.
Flink State 기반 관리
동작 방식
- Flink의 KeyedProcessFunction 또는 RichFlatMapFunction에서 ValueState, ListState, MapState 등을 활용.
- 사용자별 키(userId)로 state를 유지하고, 이벤트 처리 시 조건을 체크.
- checkpoint/restore 메커니즘을 통해 exactly-once 보장 가능.
장점
- 일관성 보장: Flink state는 checkpoint와 함께 관리되어 장애 상황에서도 데이터 유실 최소화.
- 처리 성능 최적화: 외부 호출 없이 JVM 내부 메모리/로컬 RocksDB로 state 접근 → 지연(latency) 최소화.
- 운영 단순화: 외부 저장소 장애와 무관하게 파이프라인 내에서 관리 가능.
단점
- 확장성 한계: State 크기가 커질수록 checkpoint 부하가 증가.
- 비즈니스 연계성 부족: 파이프라인 외부 서비스(예: CRM, 백오피스)에서 동일 피로도 정책 확인 어려움.
- 운영 가시성 부족: state는 Flink 내부에 있어 모니터링/조회가 불편.
- 복원 불안정 : Operator나 내부 Class등의 변경이 생길경우 복원이 정상적으로 되지 않음
보완 방법
- TTL(State Time-to-Live)을 활용해 불필요한 state 자동 정리.
- State backend(RocksDB) 튜닝으로 대규모 데이터 관리 가능.
- 필요 시 side-output 등으로 state snapshot을 외부 DB로 export하여 조회용 데이터 구축.
외부 저장소(Redis 등) 기반 관리
동작 방식
- 이벤트 처리 시 Redis에 사용자별 카운터 / 타임스탬프를 저장.
- 피로도 체크 시 Redis 조회 → 조건 충족 시 이벤트 처리, 아니면 drop.
- Redis의 INCR, EXPIRE 등을 활용하여 TTL 기반 제어 가능.
장점
- 외부 접근성 우수: CRM, 백오피스, API 등 다양한 외부 시스템에서 즉시 참조 가능.
- 운영 유연성: Redis 데이터만 보면 전체 상태 확인 가능. (Prefix 를 이용, 전체 Keys는 운영중 절대 안됨!)
- 확장성: Redis Cluster 등으로 수평 확장 가능.
- 안정성 : Flink App의 재부팅 / 결함으로부터 자유로움
단점
- 일관성 취약: Flink 처리 중 실패/재처리 시 Redis 값이 중복 증가할 수 있음.
- 성능 부담: 외부 I/O 호출로 인해 latency 증가 가능.
- 운영 복잡성: Redis 장애 시 파이프라인도 영향 받을 수 있음.
보완 방법
- 원자적 연산(GET+INCR) 사용 및 Lua Script 기반 트랜잭션 처리.
- 배치 보정 파이프라인: 하루/주 단위로 Redis 카운트를 원천 이벤트 로그와 맞추어 정합성 보정.
- 모니터링/알림 : Redis Replica/Cluster 구성을 통한 가용성 확보
보관하는 값의 종류에 따른 차이점
운영 중 겪었던 문제
처음에는 모든 데이터를 Flink state로 관리하려 했습니다.
하지만 장애 복구 시 state 복원 크기가 커지면서 문제가 발생했습니다.
- 복원 사례: 수십 GB 단위 state를 가진 잡이 재시작될 때→ 그동안 KPU(Flink 작업 슬롯)를 넉넉하게 늘려야만 안정적인 복구 가능
- → 운영 비용과 복잡성이 증가
- → Checkpoint restore 시간이 수십 분 이상 소요
이 경험을 통해 모든 값을 state에 넣는 건 정답이 아님을 깨달았습니다.
State로 관리하면 좋은 값들
State는 Flink의 강력한 무기입니다. 특히 **“스트림 내에서 바로 계산되는 값”**에는 최적화되어 있습니다.
- 누적 값 (counters): 예) 특정 키(userId)의 이벤트 누적 개수
- 평균/합/통계치 (aggregation): 예) 지난 5분간 클릭률 평균
- 세션/윈도우 기반 임시 상태: 예) 10분 세션 동안 발생한 이벤트 패턴
- 비교적 작은 사이즈의 데이터: 예) 단일 사용자 최근 5개 이벤트 저장
➡️ 이런 값들은 Flink checkpoint/restore와 exactly-once 보장이 잘 맞아떨어집니다.
외부 저장소로 관리하면 좋은 값들
반면, **“외부에서 참조가 필요하거나, 중복 증가가 치명적이지 않은 값”**은 Redis 같은 외부 저장소가 더 적합합니다.
- Fatigue (발송 제약):→ CRM/운영툴에서 실시간 확인 가능
- 중복으로 값이 과증가해도 보수적으로 동작 (안 보내는 쪽으로 기울어 안전)
- 장기 보존이 필요한 값:→ Redis/DB에 두면 조회와 보정(batch pipeline) 용이
- Flink state에 두면 checkpoint 부하 ↑
- 운영 가시성이 중요한 값:
- 운영자가 즉시 확인하고 조정해야 하는 데이터 (예: 사용자별 발송 제한 카운터)
➡️ 이런 값들은 Flink state보다 외부 저장소 + 보정 파이프라인이 더 안정적입니다
결론 및 선택 가이드
- Flink State 중심 전략: 성능과 일관성이 가장 중요할 때 선택.
- 알림 피로도 관리가 파이프라인 내부 로직에 국한되고, 외부에서 별도 조회 필요성이 적을 경우 적합.
- Redis 중심 전략: 운영 가시성, 보수적 동작, 외부 연계성
- 단, 재처리/중복 처리 리스크를 줄이기 위한 보완책이 필수.
맺음말
결국, fatigue 관리의 초점이 “실시간 정확성”인지 “운영 편의성”인지에 따라 선택지가 달라집니다.
저의 고민과 정리를 바탕으로, 비슷한 고민을 하고 계신 분들께 도움이 되길 바랍니다.
'Software Architecture' 카테고리의 다른 글
실시간 데이터 처리 구현 #1 (AWS Managed Flink + Kinesis DataStream) (1) | 2025.08.11 |
---|---|
Spring Application development guide that complies with the OAuth2 standard (0) | 2024.12.19 |
AWS 데이터베이스 비용비교 (DynamoDB vs RDS) (0) | 2024.12.19 |
Spring 기반의 Layer별 테스트케이스 작성 가이드 (0) | 2024.04.03 |
(콘웨이법칙)커뮤니케이션 구조가 소프트웨어의 구조를 결정한다. (1) | 2023.04.13 |