Netflix의 분산 카운터 추상화
전체 맥락과 큰 그림
-
Netflix의 TimeSeries Abstraction
- 시계열 이벤트 데이터를 밀리초 이하 지연으로 저장·조회할 수 있는 분산 서비스.
- Cassandra 등 다양한 저장소를 추상화해 확장성과 고성능을 제공.
-
Distributed Counter Abstraction
- 위 TimeSeries Abstraction을 기반으로 구현된 분산 카운팅 서비스.
- 다양한 카운팅 요구를 충족하기 위해 “Best-Effort”, “Eventually Consistent”, (실험적인) “Accurate” 세 가지 유형을 제공.
- Data Gateway Control Plane을 통해 샤딩, 구성, 글로벌 배포 등을 제어.
-
분산 카운터가 어려운 이유
- 정확도 vs 지연 vs 인프라 비용 사이에서 늘 트레이드오프가 존재.
- 여러 리전에 걸쳐 동일 카운터를 실시간으로 안전하게 수정·조회하는 것은 본질적으로 복잡.
- Idempotency(중복 요청에 대한 안전 보장), 중복 카운트 방지, 이벤트 순서 보장 등도 주요 과제.
주요 카운터 유형별 요약
1. Best-Effort Counter
- 핵심 아이디어: Memcached 기반 EVCache 사용 → 초저지연과 높은 처리량 확보.
- 트레이드오프:
- 교차 리전(Region) 복제 불가 → 글로벌 일관성 미흡.
- idempotency 미지원 → 안전한 재시도 어려움(중복 카운트 가능).
- “대략적인” 카운트에 충분하거나, 짧은 테스트(A/B 등)에 쓰기 적합.
코멘트:
- 짧은 시간 동안만 필요하고, 근사치만으로 충분하며, 비용 절감이 중요한 경우 이 방식을 선호.
- 하지만, 데이터 유실 혹은 중복 카운팅이 발생할 수 있으므로 업무 중요도에 따라 신중히 선택해야 함.
2. Eventually Consistent Counter
- 핵심 아이디어:
- 모든 증분(increment/decrement) 이벤트를 TimeSeries Abstraction에 기록(내구성 보장).
- 백그라운드에서 시간 창을 두고(immutable window) 주기적으로 이벤트를 “롤업(rollup)” → 최종 카운트 집계.
- 집계 결과를 Rollup Store(Cassandra)와 EVCache(캐시)에 저장하여 읽기 시 빠른 응답.
- 특징:
- idempotency 보장(이벤트에
event_id + event_time
사용). - 스케일링 시 레이스 컨디션을 허용하되, 불변 시간 창(immutable window) 덕분에 최종 수렴을 보장.
- 조금 늦은 정확성(최신 이벤트 반영 시 약간의 지연)이라는 트레이드오프.
- idempotency 보장(이벤트에
코멘트:
- 대규모 & 전 세계적으로 “거의 정확”하면서도 안전한 카운트를 필요로 할 때 최적.
- 백그라운드 집계 과정에서 로그를 모두 쌓아두므로, 감사(Audit)나 재계산 시 유리.
- “지연(잠깐 뒤처진) 정확성”은 결국 비즈니스 요구와 비용·성능 균형의 문제.
3. Accurate Counter (실험적)
- 핵심 아이디어: Eventually Consistent 카운터의 롤업 값(
lastRollupCount
) + 롤업 이후 실시간 이벤트 델타(delta
)를 조회 시 즉시 반영. - 장단점:
- 더 실시간에 가까운(더 “정확”) 값 반환 가능.
- 그만큼 이벤트 스캔(파티션 조회) 비용이 더 들어, 고부하 상황에서는 지연이 크게 증가할 수 있음.
코멘트:
- 읽기(조회) 빈도가 높아 delta 범위가 작을 때만 큰 이점을 볼 수 있음.
- 부하가 크거나 파티션이 너무 많으면 성능 저하가 심해질 수 있어, 실제 프로덕션 적용에는 주의가 필요.
핵심 구현 아이디어와 메커니즘
-
TimeSeries에 이벤트 저장
- 카운터 증분 요청마다 시점(
event_time
), 고유 식별자(event_id
) 등과 함께 기록 → 중복 방지 & 감사(추후 검증) 가능. time_bucket
,event_bucket
같은 컬럼으로 파티션 폭을 제어 → Wide Partition 문제 완화.
- 카운터 증분 요청마다 시점(
-
불변(Immutable) 시간 창 & 집계
- 쓰기가 한창 들어오는 최신 시점은 잠깐 뒤로 미뤄두고(acceptLimit), 이미 확정된 구간의 이벤트만 집계 → 레이스 없이 안전한 “Eventually Consistent” 모델.
-
백그라운드 롤업 파이프라인
- 이벤트가 들어올 때 “어떤 카운터가 수정되었는지” 가벼운 메시지를 큐에 넣음.
- 롤업 서버가 메모리 큐를 받아 일정 주기(혹은 배치 단위)마다 TimeSeries를 스캔하여 집계, 결과를 Rollup Store와 캐시에 업데이트.
- 중복 이벤트 최소화, 스레드/인스턴스 간 레이스 허용하되 최종 수렴은 보장.
-
캐시 + 스토리지의 이중 구조
- 롤업된 최종 카운트를 빠르게 조회하기 위해 EVCache 사용.
- 정확한(혹은 더 최신) 집계를 위해 Cassandra(롤업 스토어)나 TimeSeries를 필요 시 추가로 참조.
-
Control Plane 구성
- Data Gateway를 통해 네임스페이스별 카디널리티, TTL, 스토리지 유형, 큐 개수 등 다양한 설정을 일괄 관리.
- 변경 사항 적용 시 빠른 스케일 조정 가능(큐 개수, 캐시 클러스터, Cassandra 스키마 등).
향후 과제(Future Work)
-
리전 단위 롤업(Regional Rollups)
- 여러 리전에서 발생한 이벤트를 어떻게 정확히 합쳐서 글로벌 카운트를 낼 것인지.
- 리셋 이벤트 같은 특수 케이스에서 전 리전에 동기화가 필요.
-
오류 감지 및 Stale Counts 방지
- 인스턴스가 다운되거나 롤업 이벤트가 누락되면, 최신 상태로 롤업되지 않는 문제.
- 자주 사용되지 않는 카운터는 이벤트가 길게 뜸해 첫 조회에서 지연이 발생할 수 있음 → 내구성 있는 큐나 롤업 상태 승계(handoff) 방안을 검토.
-
성능·비용 최적화
- 초당 75K 이상의 요청을 안정적으로 처리하면서, 지연을 한 자릿수 밀리초로 유지.
- 스토리지 비용, 캐시 메모리 사용, 네트워크 오버헤드 등을 지속적으로 모니터링.
꼭 알아야 할 핵심 코멘트
-
정확성 vs 지연 vs 비용
- 분산 카운터 문제는 결국 이 세 가지를 어떻게 균형 맞추는지에 대한 고민.
- Best-Effort ↔ Eventually Consistent ↔ “Accurate” (실험적) 모델은 각각 다른 지점에 포지셔닝됨.
-
idempotency
- 분산 환경에서 재시도 시 중복 카운트를 막으려면 고유한 이벤트 식별자가 필수.
- 이 부분이 제대로 안 되면 장애 상황에서 카운트가 기하급수적으로 틀어질 수 있음.
-
메모리/인프라 자원 관리
- A/B 테스트처럼 카운터가 폭발적으로 늘어날 수 있는 워크로드를 상정해둬야 함.
- 대규모 트래픽에도 고정 지연을 유지하기 위해 파티션 분할, 배치 처리, 큐 병렬화 전략 등을 적용.
-
광범위 이벤트 로깅의 장점
- 감사(Audit), 재계산(Recount) 등 추적 가능성 확보.
- 다만 보관 비용이 크므로, 일정 기간 후에는 압축/삭제/이관 등 보존 정책이 필수.
-
Netflix 내부 인프라(예: EVCache, Cassandra) 재활용
- 이미 대규모로 운영 중인 스토어·캐시를 적극 활용해 효율성을 높임.
- 새로운 스토리지나 확률적 데이터 구조(HLL, CMS 등)를 직접 운영하기보다 기존 솔루션으로도 충분한 장점을 얻고 있음.
마무리
- 분산 카운팅은 쉬운 문제가 아님: 특히 전 세계 여러 리전에 걸쳐 초저지연, 높은 처리량, 높은 가용성을 동시에 추구하려면 많은 트레이드오프와 엔지니어링 노력이 필요.
- Netflix는 TimeSeries + Control Plane + EVCache + Cassandra 등을 조합해 “분산 카운팅”이라는 특정 문제를 효과적으로 해결.
- 다양한 최적화, 개선점, future work를 통해 지속적으로 발전 중.
핵심 정리:
“이벤트로 모든 증감을 저장하고, 불변 시간 구간을 정해 백그라운드 롤업을 수행하며, 캐시로 빠른 조회를 제공”
이 방식을 통해 거의 정확하면서도 높은 처리량과 낮은 지연을 양립시킬 수 있음.
댓글을 작성하려면 로그인이 필요합니다.
로그인하기