LLM WikiAccess-protected knowledge portal
← 스터디 홈
120편 · 약 13분

Spotify Random Access Parquet: AI 에이전트 시대에 데이터 레이크에서 포인트 쿼리를 제공하는 외부 인덱스 아키텍처

요약

데이터 레이크(Parquet/Iceberg)는 원래 컬럼 스캔 전용으로 설계되었다. 수억 행을 집계하는 분석 쿼리에는 탁월하지만, 특정 사용자 ID나 엔티티 키 하나를 1초 이내에 꺼내야 하는 포인트 쿼리에는 근본적으로 맞지 않는 구조다.

Spotify는 이 문제를 해결하기 위해 Random Access Parquet(RAP)를 설계했다. 핵심 아이디어는 단순하다: 데이터 자체를 복제하지 않고, 외부 인덱스가 "이 키는 이 파일의 이 바이트 오프셋에 있다"는 매핑만 저장한다. 같은 Iceberg 테이블이 분석·ML·AI 에이전트·온라인 서비스 네 가지 접근 패턴을 동시에 서빙할 수 있게 된다.

  • 공개 시점: 2026-07 (Spotify Engineering Blog)
  • 원본: https://engineering.atspotify.com/2026/07/random-access-parquet/

배경: 데이터 레이크가 포인트 쿼리에 약한 이유

Parquet 파일 구조의 근본 한계

Parquet는 행 그룹(Row Group) 단위로 데이터를 나누고, 각 컬럼을 따로 저장한다. 파일 하나에 보통 50만~100만 행이 들어가며, 특정 키 하나를 찾으려면:

  1. 파일 푸터에서 Row Group 메타데이터 읽기
  2. 컬럼별 min/max 통계로 해당 Row Group 후보군 좁히기
  3. 후보 Row Group 전체 다운로드 후 필터 적용

후보 Row Group 하나만 읽어도 수십 MB를 다운로드해야 한다. S3 레이턴시까지 더하면 단일 키 조회에 수 초가 걸린다.

일반적인 우회책과 그 비용

우회책방식문제
Redis / DynamoDB 복제별도 KV 스토어에 복제데이터 이중화, 정합성 관리 부담
소형 파케이 파일 분할키 범위별로 파일 잘게 나눔파일 수 폭발, S3 list 비용 급증
인메모리 캐시자주 쓰는 엔티티만 캐시콜드 엔티티 SLA 불충족
DuckDB/Polars 전체 로드파일 전체를 메모리에 올림메모리 한계, 테이블 크기 제약

Spotify 규모(수천 개 테이블, 각각 수억 행)에서는 어떤 우회책도 운영 부담 없이 유지되지 않았다.


RAP 아키텍처: 외부 인덱스 접근법

Iceberg 테이블 (기존 그대로) Parquet #0001 offset 0 ~ 52MB 100만 행 Parquet #0002 offset 0 ~ 48MB 95만 행 Parquet #0003 offset 0 ~ 61MB 120만 행 Iceberg 메타데이터 (manifest + snapshot) RAP 외부 인덱스 (추가 레이어) 인덱스 Fragment #1 key → (file, offset) 해시 버킷: B0~B7 인덱스 Fragment #2 key → (file, offset) 해시 버킷: B8~B15 인덱스 Fragment #3 신규 파티션용 append-only 인덱스 메타데이터 (Iceberg 커밋에 연동) 빌드 포인트 쿼리 흐름 ① 인덱스에서 (file, offset) 조회 → ② S3 Range GET (수 KB) → ③ 행 반환 AI 에이전트 온라인 서비스 ML 피처 서빙 분석 쿼리 배치 파이프라인
RAP 전체 구조: 기존 Iceberg 테이블에 외부 인덱스 레이어를 추가해 포인트 쿼리를 지원

핵심 원칙: 인덱스는 포인터만 저장한다

RAP 인덱스의 각 항목은 (lookup_key) → (parquet_file_uri, row_group_id, row_offset_in_group)의 세 값이다. 실제 데이터는 원본 Parquet 파일에 그대로 있다. 인덱스는 포인터만 관리하기 때문에:

  • 인덱스 크기가 원본 데이터 크기와 무관하게 컴팩트하게 유지된다
  • 원본 테이블의 스키마나 접근 패턴이 바뀌어도 인덱스 재빌드만 하면 된다
  • 인덱스 자체도 Parquet 포맷으로 저장되어 Iceberg 커밋에 연동된다

포인트 쿼리 흐름:

  1. 인덱스에서 키로 (file_uri, row_group_id, row_offset) 조회 — 인덱스 파일은 수십 MB, 메모리에 올릴 수 있음
  2. S3 Range GET으로 해당 Row Group의 해당 행만 수 KB 다운로드
  3. 행 반환 — 전체 과정 목표 레이턴시 100ms 이내

전체 Row Group 다운로드 없이 필요한 행만 바이트 수준으로 접근하는 것이 핵심이다.


인덱스 빌드 전략 세 가지

전략 1: 해시 버킷팅 (Hash Bucketing)

키를 해시해서 버킷 번호를 결정하고, 같은 버킷의 키들을 같은 인덱스 파일에 모은다. 버킷 번호가 파일을 결정하기 때문에 인덱스 파일 자체를 전부 읽지 않아도 어느 파일을 봐야 할지 계산으로 알 수 있다.

bucket = hash(lookup_key) % NUM_BUCKETS
index_file = s3://index-bucket/table/bucket={bucket}/part-00000.parquet

장점: 인덱스 조회가 한 번의 파일 접근으로 끝남 단점: NUM_BUCKETS 변경 시 전체 인덱스 재빌드 필요

전략 2: 코-그루핑 (Co-grouping)

원본 데이터를 Iceberg에 쓸 때 같은 키의 모든 행을 같은 Parquet 파일에 몰아넣는다. 키 기준으로 파티셔닝해서 쓰는 방식이다.

장점: 파일 하나를 보면 그 키의 모든 이력/버전을 얻을 수 있음 (타임시리즈 ML 피처에 유리) 단점: 쓰기 파이프라인에 키 기준 정렬이 필요, 데이터 쏠림 위험

전략 3: 정렬 기반 (Sort-based)

Parquet 파일 내 Row Group을 키 기준으로 정렬해서 쓴다. 인덱스 없이도 이진 탐색으로 Row Group을 좁힐 수 있지만, RAP는 여기에 인덱스를 더해 이진 탐색 자체도 건너뛴다.

장점: 범위 쿼리(range scan)와 포인트 쿼리를 동시에 지원 단점: 정렬 컬럼 외의 키로 조회 시 효과 없음

실제 운영에서는 세 전략을 조합한다. 기본 사용자 ID 조회는 해시 버킷팅, ML 피처 타임시리즈는 코-그루핑, 시간 범위 스캔이 함께 필요한 테이블은 정렬 기반을 선택한다.


인덱스 빌더: Iceberg 커밋에 연동되는 append-only 구조

Iceberg는 새 데이터가 추가될 때마다 새 스냅샷을 만든다. RAP 인덱스 빌더는 이 Iceberg 커밋 이벤트를 구독해서 추가된 Parquet 파일에 해당하는 인덱스 Fragment만 새로 생성한다.

새 Iceberg 스냅샷 감지
  → 신규 Parquet 파일 목록 추출
  → 각 파일 스캔: (key, file_uri, row_group_id, offset) 추출
  → 인덱스 Fragment Parquet 파일 생성 및 S3 업로드
  → 인덱스 메타데이터 업데이트 (Iceberg 커밋에 연동)

이 구조의 장점:

  • 증분 빌드: 전체 테이블을 다시 스캔하지 않는다. 새 파일에 대한 Fragment만 추가한다
  • 원자성: Iceberg 커밋 단위와 인덱스 Fragment가 1:1로 대응된다. 인덱스와 데이터의 정합성을 Iceberg 스냅샷 ID로 보장한다
  • 이전 버전 호환: 인덱스가 없는 구 파티션은 기존 스캔 방식으로 폴백한다

AI 에이전트 시대의 포인트 쿼리 급증

왜 지금 이 문제가 중요해졌는가

2025~2026년에 접어들면서 Spotify 같은 대형 플랫폼은 AI 에이전트 파이프라인에서 포인트 쿼리 폭발을 경험하기 시작했다.

분석 쿼리는 "사용자 1억 명의 청취 패턴을 집계해줘" 같은 형태다. 이건 Parquet가 잘 하는 일이다. 하지만 에이전트 추론 루프는 다르다: "사용자 A의 최근 10개 청취 이력 → A가 좋아한 아티스트의 팬 유사도 → 상위 5명 사용자 각각의 플레이리스트 → 각 플레이리스트의 최신 트랙 메타데이터 → …"

이런 체인에서 각 단계는 1개~수십 개 키의 포인트 조회다. 에이전트가 추론 중에 수백 번의 개별 엔티티 조회를 날리는 패턴이 일반화되고 있다.

기존 온라인 서빙 스택과의 차이

기준Redis/DynamoDB 복제RAP 외부 인덱스
데이터 신선도복제 파이프라인 지연Iceberg 커밋 즉시
스키마 관리2개 시스템 이중 관리단일 Iceberg 스키마
쿼리 유연성KV 조회만컬럼 프로젝션 자유
비용KV 스토어 상시 유지 비용S3 + 인덱스 파일만
데이터 복제필수없음 (포인터만)
콜드 엔티티 SLA동일동일

에이전트 파이프라인이 요구하는 "최신 데이터 + 다양한 컬럼 접근 + 낮은 레이턴시" 조합에서 RAP가 Redis 복제보다 운영 부담이 작다.


Iceberg와의 통합

RAP는 Iceberg의 기존 메타데이터 구조를 활용한다:

  • Iceberg Table Properties: 테이블마다 어떤 컬럼을 인덱스 키로 쓸지, 어떤 버킷 전략을 쓸지 설정
  • Iceberg Custom Metadata: 각 스냅샷에 해당 인덱스 Fragment의 S3 URI를 첨부
  • Iceberg REST Catalog: 인덱스 메타데이터를 Catalog를 통해 조회 — 별도 인덱스 관리 서비스 불필요

Spark/Trino 같은 기존 쿼리 엔진은 인덱스를 인식하지 못해도 Iceberg 테이블을 그대로 읽는다. RAP 클라이언트만 인덱스를 활용한 포인트 쿼리 경로를 택한다. 기존 배치 파이프라인과 분석 쿼리는 영향 없다.


운영 시 고려 사항

인덱스 재빌드 비용

컬럼 추가, 버킷 수 변경, 키 컬럼 변경 시 전체 또는 부분 인덱스 재빌드가 필요하다. Spotify는 Iceberg Time Travel을 활용해 특정 스냅샷부터 다시 빌드하는 방식을 쓴다.

재빌드 트리거:

  • 버킷 수 변경 (NUM_BUCKETS 수정)
  • 인덱스 키 컬럼 변경
  • 인덱스 Fragment 오염 감지 (체크섬 불일치)

인덱스 Fragment 관리

인덱스 Fragment는 Iceberg 스냅샷 lifecycle을 따른다. expire_snapshots를 실행하면 해당 스냅샷의 Fragment도 함께 만료된다. 오래된 인덱스 파일을 별도로 정리할 필요가 없다.

레이턴시 목표와 캐싱

S3 Cold Read 기준 단일 포인트 쿼리 목표: 100ms 이내. 반복 접근이 잦은 인덱스 파일은 로컬 캐시(예: Alluxio, Caffeine)를 통해 10ms 이내로 내릴 수 있다.

인덱스 파일 크기가 작으면(수 MB~수십 MB) 조회 서비스 인스턴스 메모리에 전체 인덱스를 로드하는 것도 가능하다.

동시성과 정합성

Iceberg의 낙관적 잠금(optimistic concurrency control)을 사용하므로 여러 빌더가 동시에 Fragment를 생성해도 충돌이 자동 해소된다. 동일 Parquet 파일에 대해 중복 Fragment가 생성되면 메타데이터 업데이트 단계에서 감지되어 하나만 커밋된다.


체크리스트

  • [ ] 포인트 쿼리가 필요한 테이블 목록을 Iceberg 메타데이터에서 식별했는가
  • [ ] 인덱스 키 컬럼의 카디널리티와 분포(핫키 위험)를 사전에 측정했는가
  • [ ] 버킷 전략(해시/코-그루핑/정렬)을 접근 패턴에 맞춰 선택했는가
  • [ ] 인덱스 빌더가 Iceberg 커밋 이벤트를 구독하도록 파이프라인을 구성했는가
  • [ ] 인덱스 Fragment와 Iceberg 스냅샷 expire 정책을 연동했는가
  • [ ] RAP 클라이언트의 폴백 경로(인덱스 없으면 풀 스캔)를 검증했는가
  • [ ] 레이턴시 SLA(100ms 이내)를 달성하기 위해 캐싱 레이어를 설계했는가
  • [ ] 기존 Spark/Trino 파이프라인이 RAP 도입 후에도 정상 동작함을 확인했는가

References

  • Spotify Engineering Blog, "Random Access Parquet" (2026-07): https://engineering.atspotify.com/2026/07/random-access-parquet/
  • InfoQ, "Spotify Introduces Random Access Parquet for Low-Latency Point Queries on Data Lakes" (2026-08)
  • Apache Iceberg 공식 문서 — Table Properties, Custom Metadata: https://iceberg.apache.org/docs/latest/configuration/
  • Apache Parquet 파일 포맷 스펙 — Row Group, Page, Statistics: https://parquet.apache.org/docs/file-format/
  • amdatalakehouse.substack.com, "Weekly: Spotify RAP, Iceberg v3, and Point Query Patterns" (2026-08)