Lake Point Query Indexing

데이터 레이크는 더 이상 배치 전용이 아니다 — 하나의 데이터셋, 두 개의 접근 패턴


핵심 개념

온라인 서비스와 AI 에이전트가 같은 프리미티브를 필요로 한다: KV 스토어에 경제적으로 상주시키기엔 너무 큰 데이터셋에 대한 키 기반 빠른 point query.

  • 포털·개인화 기능은 사용자별 데이터를 대화형 속도로 조회·페이지네이션해야 한다
  • “작년 여름에 뭘 들었지?”에 답하는 에이전트는 사용자 데이터를 빠르게 가져와 필터·집계하거나 로컬에서 SQL을 돌려 LLM 프롬프트용 컨텍스트를 만들어야 한다

병목이 이동했다

레이크 기저 스토리지는 빠르고 더 빨라지고 있다. GCS가 요청당 30–100ms, S3 Express One Zone과 GCS Rapid Storage는 한 자리수 밀리초다.

병목은 점점 스토리지 레이어 자체가 아니라 그 위의 쿼리 엔진이다.

Trino·BigQuery 같은 분산 SQL 엔진은 단일 row 조회에도 초 단위의 잡 스케줄링·쿼리 플래닝 오버헤드를 더한다. 분석 처리량용으로 설계되었지 대화형 point query용이 아니다.

왜 파티셔닝 + Bloom filter로 부족한가

표준 기법이 후보를 크게 줄여준다.

90일 × 일 1,000 파일 = 90,000 파일
  ↓ 키로 버킷 파티셔닝 (파일명만으로 키 존재 가능성 판단)
90 파일
  ↓ Bloom filter (파일을 열지 않고 버림)
12 파일 (사용자가 실제 활동한 날)

하지만 그 12개 파일은 여전히 읽어야 하고, 파일 안에서 한 키의 데이터를 찾으려면 의존적 read 체인이 필요하다.

footer 가져오기 → row group 메타데이터 파싱 → 키 컬럼 스캔으로 매칭 row 찾기
  → column·page index로 각 값 컬럼의 해당 페이지 찾기

파일당·컬럼당 다중 왕복 의존 fetch이고 모든 왕복이 다른 동시 쿼리와 IOPS를 다툰다. 파티셔닝과 Bloom filter는 파일 선택을 돕지만 파일 안의 문제를 풀지 못한다.

의존적 read 체인이 근본 병목이다

각 링크가 두 가지를 소비한다.

  • 레이턴시 — 다음이 시작하기 전 한 번의 왕복
  • 대역폭 — 어디를 읽을지 알아내려고 읽는 바이트

클라우드 스토리지에서 링크당 수십 ms, 로컬 SSD에서 마이크로초, 메모리에서 나노초. 절대값은 달라도 구조는 같고, 체인을 무너뜨려 의존적 read를 단일 사전계산 조회로 대체하면 어느 티어에서든 레이턴시와 대역폭을 모두 절약한다.

외부 인덱스 — 스캔 대신 조회

키 → [외부 인덱스] → (파일, row 번호)
    → 캐시된 파일 메타데이터로 페이지 위치 해소
    → 필요한 페이지만 정확히 가져오는 ranged read (병렬 발행 가능)

인덱스 조회는 O(1), 캐시된 페이지 매핑은 저지연, 데이터 검색은 소수의 정밀한 ranged read다. 결정적으로 의존적 로드 체인이 없다.

인덱스의 성질

  • 어떤 기존 Parquet 파일에서도 특별한 준비 없이 동작한다
  • 새 데이터가 오면 각 파이프라인 실행이 대응 항목을 생성하고, 인덱스는 기존 fragment를 수정하지 않고 append로 성장한다
  • multimap — 키 하나가 여러 파일·파티션에 항목을 가진다. 각 항목은 compact: key / file(dictionary 인코딩 ordinal) / row numbers / (선택) value count
  • 대략 테라바이트 → 기가바이트, 페타바이트 → 테라바이트의 인덱스. 큰 인덱스는 hash bucketing으로 분산

Parquet 내장 구조와의 차이

PageIndex와 Bloom filter는 확률적이고 스캔을 좁힐 뿐이다. 외부 인덱스는 결정적(definitive) 이다 — 키가 주어지면 정확한 파일과 row를 반환해 스캔 자체를 제거한다.

트레이드오프의 균형이 바뀐다

외부 인덱스는 리더가 파일을 만지기 전에 정확한 파일·row·컬럼을 알게 한다. 따라서:

덜 중요해지는 것더 중요해지는 것
세밀한 page index적은 왕복
술어 skipping용 작은 페이지작은 fetch
pushdown용 dictionary 인코딩연속된 데이터
(파일 내 발견을 돕는 성질)(최종 read를 최소화하는 성질)

최적화 3범주

1. 키 데이터 집중

기법point-lookup 이득분석 트레이드오프
키로 정렬키당 파일·페이지 수 감소없음
co-grouping (키당 한 row, 값을 반복/중첩 컬럼에)자연히 집중, 정렬 순서 불필요없음
더 거친 파티셔닝 (일별 365 → 주별 52 파일/키/년)인덱스 항목·병렬 read·인덱스 크기 감소파티션 프루닝 세밀도 저하

2. 읽는 바이트 감소

  • one page per key — writer가 키 경계에서 페이지를 flush. 압축 해제된 페이지 전체가 타겟 키에 속하므로 row 추출이 불필요하고, 페이지 위치를 인덱스 항목에 직접 저장할 수 있어 별도 page index도 필요 없다. 파일은 표준 Parquet으로 남는다. 키 경계마다 약 20바이트 헤더 오버헤드
  • ZSTD frame reset — one-page-per-key가 PageIndex를 부풀릴 때의 대안. 관례적 페이지 크기를 유지하되 각 키의 row를 페이지 안의 별도 ZSTD frame으로 압축. 표준 리더에게는 페이지가 정상적으로 압축 해제되고(ZSTD frame은 단순 연결) RAP에게는 각 frame이 독립 주소 지정 가능. 대가는 frame이 앞 frame 없이 해석 가능해야 해서 delta·run-length 인코딩이 배제되는 것
  • storage alignment — ZSTD skippable frame으로 키 사이를 패딩해 fetch를 블록 경계에 정렬. 경계를 걸치는 fetch는 두 블록을 읽어야 한다

3. read 연산 수 감소 — 가장 큰 단일 승리

표준 Parquet에서 N개 컬럼의 값을 가져오려면 N개의 병렬 read가 필요해 요청 용량을 N배 소비하고 키당 레이턴시를 분포의 꼬리로 밀어낸다.

  • Blob·Variant — 랜덤 접근에 필요한 필드를 단일 컬럼(JSON, Protobuf, Parquet Variant)으로. 온라인 애플리케이션이 데이터를 개별 필드가 아니라 문서로 소비하는 방식과 자연스럽게 맞는다. 대가는 blob 내부의 필드별 프루닝·pushdown 상실
  • 컬럼 interleaving — 키의 여러 컬럼 데이터를 물리적으로 인접 배치하고 ZSTD skippable frame으로 이어준다. 관례적 리더는 컬럼 A를 순차 읽으며 압축 해제기가 skip frame 안의 B를 조용히 건너뛰고, RAP 리더는 키의 모든 컬럼에 걸친 단일 연속 ranged read를 발행한다. 사실상 후방 호환성을 깨지 않고 선택 컬럼 그룹을 row-major로 pivot하는 것. 대가는 단일 컬럼 스캔의 I/O 볼륨 증가
  • covering index / hoisted value — 인덱스 빌더가 빌드 시점에 모든 row를 방문하므로 작은 값을 인덱스 항목에 직접 hoist할 수 있다. read 수를 0으로 만드는 covering index다. 사전 계산 집계(키별 이벤트 수, 총 시간, 카테고리 분해)로 확장되고, 인덱스 레벨의 술어 pushdown(스토리지 read 전에 필터링)도 가능해진다

이들을 합치면 point query가 몇 킬로바이트의 단일 ranged read로 줄거나 스토리지 read가 아예 사라진다.

Secondary index

데이터셋이 여러 조회 차원을 가질 수 있다(buyer_id와 seller_id). 같은 인덱스 항목 위에 차원마다 접근 구조를 만든다 — 해시 테이블은 O(1) 정확 조회, 정렬 인덱스는 범위 쿼리.

추가·제거가 서빙 레이어 결정이라 파이프라인 변경도 데이터 재작성도 없다. 파일 레이아웃은 정렬된 차원을 선호하고 secondary 조회는 더 많은 파일에 흩어지지만 리더가 인접 바이트 범위를 자동 coalesce한다. Z-ordering·Hilbert curve는 보완적이다 — 파일 레이아웃 레벨에서 지역성을 개선하고, secondary index가 접근 경로를 제공한다.

왜 이것이 경제학을 바꾸는가

핵심 성질은 이미 레이크에 있는 같은 Parquet 파일에서 동작한다는 것이다. BigQuery가 주간 리포트를 위해 스캔하는 파일이 AI 에이전트가 컨텍스트 검색을 위해 읽는 바로 그 파일이다. 복사도, ETL도, 두 번째 스토리지 청구서도 없다.

오늘날 팀은 GB당 비용이 우선순위를 강제하기 때문에 KV 스토어에 무엇을 둘지 어려운 선택을 한다. 이 접근에서는 point query 비용이 클라우드 스토리지 read 비용으로 떨어진다. 히스토리 데이터, 롱테일 엔티티, 저트래픽 피처가 모두 대화형 접근에 실현 가능해진다.

AI 에이전트가 컨텍스트를 KV 스토어가 담은 것으로 제한할 필요 없이 레이크 전체 폭에 걸쳐 몇 달, 몇 년을 되짚을 수 있다.

Puffin-Backed Derived Data와의 비교

둘 다 “테이블 포맷 위에 파생 접근 구조를 얹는다”는 문제를 다루지만 방향이 다르다.

Lake point query indexingPuffin-backed index
인덱스 저장외부(별도 인덱스 스토어)테이블 포맷 안(Puffin blob)
생명주기파이프라인 실행마다 append스냅샷에 바인딩
대상정확한 키 조회근사 최근접 이웃(ANN)
이점준비 없는 기존 파일에서도 동작원자성·시간여행·GC를 공짜로 상속

연관 개념


Source: Indexing the Data Lake for Online Point Queries