같은 질문에 두 번 답하지 말자: Netflix 규모 Druid를 위한 구간 인지 캐싱
핵심 내용
Netflix는 Druid 롤링 대시보드의 중복 쿼리를 구간 인지 캐싱으로 줄였다.
자세히 보기
Netflix는 10조 건이 넘는 데이터를 다루는 Druid에서 롤링 윈도 대시보드가 만들어내는 중복 쿼리 폭증을 해결하기 위해, 시간 구간을 이해하는 캐싱 계층을 실험적으로 도입했다.
문제의 핵심은 같은 대시보드가 10초마다 갱신되면서 거의 같은 쿼리를 반복한다는 점이다. 예를 들어 26개 차트로 구성된 인기 대시보드는 한 번에 64개 쿼리를 만들고, 이것을 30명이 동시에 보면 초당 192개 쿼리가 발생한다.
기존의 full-result cache와 per-segment cache는 롤링 윈도처럼 조금씩 이동하는 시간 범위에는 잘 맞지 않았다. 특히 실시간 세그먼트가 포함되면 Druid는 결정성과 정확성을 위해 결과를 캐시하지 않기 때문에, 동일한 데이터에 대한 반복 질의가 그대로 Druid에 부담으로 쌓였다.
해결책은 오래된 구간은 재사용하고, 가장 최근의 불안정한 구간만 Druid에 새로 묻는 방식이다. 캐시는 쿼리의 시간 구간을 제외한 형태를 SHA-256으로 해시해 키로 쓰고, 내부적으로는 시간값을 1분 또는 쿼리 granularity 중 더 큰 단위로 bucketting한 map-of-maps 구조를 사용한다.
동작 방식은 다음과 같다.
- Router 가로채기: Druid Router에서 요청을 받아 캐시가 처리할 수 있으면 먼저 응답한다.
- 부분 히트 처리: 캐시가 전체를 못 채우면, 캐시된 시작 구간 뒤의 missing tail만 좁혀서 Druid에 다시 보낸다.
- 결합: 캐시 결과와 새 결과를 timestamp 순으로 합쳐 동일한 JSON 형태로 반환한다.
- 비동기 기록: Druid에서 받은 새 데이터는 bucket 단위로 쪼개어 캐시에 비동기로 다시 저장한다.
정확도와 신선도는 5초 TTL이라는 명시적 trade-off를 받아들이는 대신 확보했다. 게다가 데이터가 오래될수록 더 안정적이라는 점을 반영해, 2분 미만 데이터는 최소 5초 TTL을 두고, 그 이후에는 1분 더 오래될 때마다 TTL을 두 배로 늘려 최대 1시간까지 확장한다.
희소한 지표를 위해서는 negative caching도 넣었다. 중간에 실제로 비어 있는 bucket은 빈 sentinel 값으로 저장해 불필요한 재질의를 막되, 아직 데이터가 도착하지 않았을 수 있는 trailing empty bucket은 캐시하지 않아 late-arriving data를 잘못 가리지 않도록 했다.
백엔드는 KVDAL을 쓰고, 저장소는 Cassandra다. KVDAL의 2단계 맵과 inner key별 독립 TTL은 이 구조에 잘 맞고, 결과적으로 Netflix는 롤링 대시보드의 반복 질의를 크게 줄이면서도 실시간 분석의 신선도 요구를 받아들일 수 있는 캐싱 계층을 얻었다.
이 한국어 요약은 AI가 자동으로 만들었습니다. 원문의 주장과 맥락은 원문에서 확인해 주세요. 저작권은 원저작자에게 있습니다.