Loon 소개: 끊임없이 변화하는 벡터 데이터를 위한 새로운 스토리지 엔진
핵심 요점
이 글은 길고 심층적인 엔지니어링 분석이므로, 세부 내용으로 들어가기 전에 핵심 사항을 먼저 정리합니다.
- AI 데이터셋은 정적인 테이블이 아닙니다. 팀이 임베딩 모델을 교체하고, 희소 벡터를 추가하고, 캡션을 수정하고, 라벨을 백필하고, 인덱스를 다시 구축하고, 오프라인 분석을 실행함에 따라 동일한 행이 계속 변경됩니다.
- 기존 스토리지 레이아웃은 세 가지 방식으로 한계에 부딪힙니다. 긴 벡터 컬럼은 백필 비용을 높이고, 단일 파일 형식은 스캔과 포인트 읽기를 모두 잘 지원할 수 없으며, 프라이빗 데이터베이스 스토리지는 외부 파이프라인이 진실의 사본을 추가로 만들도록 강제합니다.
- Loon은 Milvus와 Zilliz Vector Lakebase를 위한 새로운 스토리지 엔진입니다. 하이브리드 파일 형식, 행 ID 정렬, 그리고 데이터셋의 버전 관리 상태를 정의하는 Manifest를 중심으로 구축되었습니다.
- 목표는 단일 벡터 데이터셋이 데이터를 지속적으로 복사하거나, 다시 쓰거나, 다시 가져오지 않고도 온라인 검색, 오프라인 분석, 백필, 컴팩션, 외부 컴퓨팅을 지원할 수 있도록 하는 것입니다.
소개
한동안 벡터 데이터베이스에 반대하는 주장 중에는 그럴듯하게 들리는 것이 하나 있었습니다.
기존 데이터베이스는 이미 정수, 문자열, JSON, blob, 인덱스를 저장합니다. 그렇다면 _vector_ 타입을 추가하고, 그 옆에 ANN 인덱스를 만들고, 그것으로 충분하다고 하면 되지 않을까요?
초기 시맨틱 검색의 경우에는 이 방식으로도 충분히 잘 작동합니다. 벡터 컬럼과 인덱스는 데모, 작은 RAG 애플리케이션, 또는 내부 검색 기능을 지원할 수 있습니다. 문제는 나중에, 데이터셋이 테이블이라기보다 AI 데이터 시스템처럼 동작하기 시작할 때 나타납니다.
프로덕션 벡터 데이터셋에는 행, 기본 키, 스칼라 필드, 쿼리 가능한 컬럼이 있습니다. 그런 의미에서는 데이터베이스 테이블처럼 보입니다. 하지만 동시에 데이터 레이크와 같은 규모와 워크플로 형태도 가지고 있습니다. 수억 개의 레코드를 포함할 수 있습니다. Spark, Ray, DuckDB, 학습 파이프라인, 평가 작업, 데이터 품질 시스템에 의해 반복적으로 읽히고 다시 쓰입니다.
또한 객체 스토리지에 의존합니다. 원본 객체는 흔히 S3, GCS, OSS 또는 다른 객체 저장소에 남아 있는 비디오, 이미지, PDF, 오디오 파일, 웹 문서입니다. 데이터베이스는 참조, 메타데이터, 파생 피처, 인덱스를 저장합니다. 그런 다음 기존 스토리지 모델이 일급 객체로 관리하도록 설계되지 않았던 것들을 추가합니다. 밀집 임베딩, 희소 벡터, 캡션, 벡터 인덱스, 텍스트 인덱스, 삭제 로그, 통계, 모델 버전, 파서 버전, 외부 blob 참조, 그리고 이 모든 것 사이의 버전 관계입니다.
바로 이 지점에서 “그냥 벡터 컬럼을 추가하면 된다”는 접근이 무너지기 시작합니다. 문제는 데이터베이스가 벡터 바이트를 저장할 수 있는지 여부가 아닙니다. 많은 시스템이 그렇게 할 수 있습니다. 더 어려운 질문은 스토리지 모델이 벡터 데이터가 변화하는 방식, 쿼리되는 방식, 그리고 AI 데이터 스택 전반에서 공유되는 방식을 처리할 수 있는지 여부입니다.
이것이 우리가 Milvus와 Zilliz Vector Lakebase (Zilliz Cloud의 다음 진화 단계)를 위한 새로운 스토리지 엔진인 Loon을 만든 이유입니다.
Loon은 세 가지 아이디어를 바탕으로 설계되었습니다.
- 컬럼 종류에 따라 서로 다른 물리적 형식을 사용합니다.
- 공유 행 ID 공간을 통해 해당 컬럼들을 정렬합니다.
- Manifest를 사용하여 데이터셋의 버전 관리 상태를 정의합니다.
이 요소들이 왜 중요한지 이해하기 위해, 일반적인 멀티모달 워크플로부터 살펴보겠습니다.
벡터 데이터셋은 결코 실제로 완성되지 않습니다.
멀티모달 학습을 위한 비디오 데이터셋을 구축하는 AI 팀을 상상해 보겠습니다.
긴 비디오가 객체 스토리지에 업로드됩니다. 파이프라인은 장면 전환, 샷 경계, 또는 시간 창을 기준으로 비디오를 클립으로 자릅니다. 너무 길거나 너무 짧은 클립, 흐릿한 클립, 중복된 클립, 품질이 낮은 클립은 필터링됩니다. 남은 클립은 미적 평가 모델로 점수를 매기고, 다른 모델로 캡션을 생성하며, 비전-언어 모델로 임베딩한 뒤, 검색, 중복 제거, 학습 데이터 필터링을 위해 벡터 데이터베이스에 저장됩니다.
높은 수준에서 보면 워크플로는 단순해 보입니다.
video
→ clips
→ metadata
→ aesthetic_score
→ caption
→ embedding
→ search / dedup / training data filtering
하지만 데이터셋은 완성된 형태로 도착하지 않습니다.
- 첫째 주에는 테이블에
clip_id,video_id,start_offset,duration만 포함될 수 있습니다. - 둘째 주에는 팀이
aesthetic_score를 추가합니다. - 셋째 주에는 캡셔닝 모델이 실행되고, 각 클립에
caption이 생깁니다. - 넷째 주에는 첫 번째 임베딩 모델이 온라인 상태가 되고, 각 클립에 768차원 CLIP 임베딩이 생깁니다.
- 한 달 후, 팀은 모델을 전환하고
embedding_v2를 백필하며, 이제 차원 수는 1024가 됩니다. - 두 달 후, 하이브리드 검색이 요구사항이 되면서 팀은 희소 벡터 컬럼을 추가합니다.
- 세 달 후, 캡션은 사람의 검토를 거치며 제자리에서 수정되어야 합니다.
데이터셋은 결코 완성되지 않았습니다. 동일한 기본 행에 대한 새로운 해석이 계속 누적되었습니다.
이것이 벡터 데이터와 전통적인 비즈니스 데이터의 핵심 차이점 중 하나입니다. 동일한 행이 계속해서 다시 처리됩니다. 그리고 규모는 이것을 불편함에서 스토리지 문제로 바꿉니다. 멀티모달 데이터셋은 종종 수백만 건의 레코드가 아니라 수억 또는 수십억 건입니다. LAION-5B는 그 형태를 이해하는 데 유용한 참고 사례입니다. 수십억 개의 이미지-텍스트 쌍, 각각에 메타데이터, 캡션, 임베딩이 포함되어 있습니다. 따라서 어려운 부분은 첫 번째 삽입이 아닙니다. 어려운 부분은 데이터셋이 진화하기 시작한 뒤에 발생하는 모든 것입니다. 그 진화는 세 가지 문제를 드러냅니다.
첫 번째 문제: 긴 컬럼은 쓰기 증폭을 비싸게 만든다
Parquet과 같은 컬럼형 포맷은 많은 분석 워크로드에 매우 적합합니다. 스키마가 상당히 안정적이고, 데이터가 다시 쓰이는 것보다 더 자주 읽히며, 스캔이 컬럼의 일부만 건드리고, 압축이 중요한 경우 잘 작동합니다. 이것이 많은 분석 포맷이 최적화되어 온 세계입니다.
벡터 행은 분석 행보다 훨씬 넓다
TPC-H lineitem은 좋은 기준선입니다. 16개의 컬럼이 있습니다. 정수 키, 십진수 값, 날짜, 짧은 문자열, 작은 코멘트 필드입니다. 압축되지 않은 행 하나는 대략 150바이트입니다. 압축 후에는 훨씬 더 작을 수 있습니다. 64 MB 행 그룹을 사용하면 스토리지 시스템은 하나의 그룹에 수십만 개의 행을 담을 수 있습니다.
벡터 데이터셋은 그렇게 생기지 않았습니다.
LAION 스타일의 이미지-텍스트 데이터셋은 오늘날 많은 AI 파이프라인이 생성하는 것에 훨씬 더 가깝습니다. 각 행에는 여전히 일반적인 메타데이터가 있습니다. URL, 캡션, 너비, 높이, 품질 점수, 레이블 등이 있습니다. 하지만 임베딩이 추가되면 행의 물리적 형태가 바뀝니다.
768차원 CLIP 벡터는 fp16에서 약 1.5 KB, fp32에서 3 KB 정도를 차지합니다. 그 하나의 컬럼이 전체 TPC-H lineitem 행보다 훨씬 클 수 있습니다.
그리고 768차원은 오늘날 기준으로 특이하거나 큰 것이 아닙니다. 1024차원 또는 2048차원 임베딩은 멀티모달 파이프라인에서 흔합니다. OpenAI의 text-embedding-3-large는 최대 3072차원까지 올라가며, 이는 fp32에서 벡터당 약 12 KB입니다.
비교는 뚜렷합니다.
| 데이터셋 형태 | 대략적인 행 크기 | 지배적인 필드 |
|---|---|---|
| TPC-H lineitem | 압축 전 ~150바이트 | 스칼라 및 짧은 문자열 |
| 768차원 fp16 벡터가 있는 LAION 스타일 행 | ~1.5 KB+ | 임베딩 |
| 768차원 fp32 벡터가 있는 LAION 스타일 행 | ~3 KB+ | 임베딩 |
| 3072차원 fp32 벡터가 있는 행 | 벡터만 ~12 KB+ | 임베딩 |
많은 AI 데이터셋에서 벡터 컬럼은 그저 또 하나의 필드가 아닙니다. 물리적으로는 행의 대부분입니다. 이는 스키마 진화의 비용을 바꿉니다.
벡터 컬럼 하나를 추가하는 데 수백 기가바이트가 필요할 수 있다
데이터셋에 1억 개의 비디오 클립이 있다고 가정해 보겠습니다. 새로운 1024차원 fp32 임베딩 컬럼을 추가한다는 것은 대략 400 GB의 원시 벡터 데이터를 쓰는 것을 의미합니다. 여기에는 통계, 인덱스, 메타데이터 업데이트, 객체 스토리지 오버헤드, 검증, 서빙 경로 통합이 포함되지 않습니다.
팀이 매달 embedding_v2, sparse_vector 또는 rerank 기능과 같은 벡터 유사 열을 한두 개씩 추가한다면, 스키마 진화는 수백 기가바이트 또는 테라바이트 단위로 측정되는 반복적인 데이터 엔지니어링 작업이 됩니다.
작은 논리적 업데이트가 큰 물리적 재작성을 유발할 수 있음
업데이트도 그만큼 중요합니다.
컬럼형 시스템에서는 일반적으로 기존 데이터가 제자리에서 업데이트되지 않습니다. 삭제 로그가 변경된 내용을 기록하고, 이후 compaction이 살아 있는 행을 새 파일로 다시 씁니다. 이 모델은 행이 작을 때는 관리 가능합니다.
벡터 데이터에서는 작은 논리적 업데이트가 큰 물리적 재작성을 유발할 수 있습니다.
사람이 검토하는 작업은 캡션에서 몇백 바이트만 수정할 수도 있습니다. 하지만 캡션, dense vector, sparse vector 및 기타 파생 기능이 동일한 물리적 파일 수명 주기를 공유한다면, 시스템은 결국 벡터까지 다시 쓰게 될 수 있습니다. 논리적 변경은 작습니다. 물리적 I/O는 엄청날 수 있습니다.
이것이 벡터 스토리지의 쓰기 증폭 문제입니다. 비용이 많이 드는 부분은 벡터가 크다는 점뿐만이 아닙니다. 큰 파생 필드와 작고 변경 가능한 필드가 하나의 단위로 취급되는 스토리지 레이아웃에 의해 함께 묶이는 경우가 많다는 점입니다.
AI 데이터셋에서는 backfill이 일상적인 워크로드임
전통적인 분석 테이블에서는 스키마 진화가 가끔만 발생할 수 있습니다. AI 데이터셋에서는 일상적입니다. 캡션 모델이 업그레이드됩니다. 임베딩 모델이 교체됩니다. Sparse vector가 나중에 추가됩니다. Rerank 기능이 나타납니다. 사람의 라벨이 수정됩니다. 거버넌스 태그가 backfill됩니다. 인덱스가 재구축됩니다.
이러한 작업은 단순한 append가 아닙니다. 기존 행을 자주 수정하거나 확장합니다.
그래서 벡터 스토리지는 scan 처리량만 최적화할 수 없습니다. 또한 backfill과 부분 업데이트의 비용을 낮춰야 합니다.
두 번째 문제: 동일한 데이터가 scan과 point read를 지원해야 함
데이터가 기록된 후 read path는 분기됩니다. 동일한 벡터 데이터셋에는 일반적으로 두 가지 서로 다른 접근 패턴이 있습니다: 분석적 scanning과 point read.
분석 워크로드는 넓고 압축된 scan을 원함
파이프라인은 다음과 같은 필터를 실행할 수 있습니다:
WHERE aesthetic_score > 0.8 AND duration > 5
또는 오프라인 분석, 전체 임베딩 평가, BM25 통계, bitmap 생성, 데이터 품질 검사, count 및 group-by를 실행할 수 있습니다.
이 패턴은 많은 행을 읽지만 열은 몇 개만 읽습니다. 순차 I/O, 더 큰 row group, 압축, column pruning, batch decoding 및 vectorized execution을 선호합니다.
큰 row group은 여기서 도움이 됩니다. 단일 I/O 요청으로 많은 양의 유용한 데이터를 가져올 수 있고, 압축 효율을 높이며, 실행 엔진에 오버헤드를 상쇄할 만큼 충분한 연속 데이터를 제공합니다. 여러 열을 함께 읽을 때, scan 처리량을 위해 정리된 상태를 유지하면 vectorized execution 중 cache miss를 줄이는 데에도 도움이 됩니다.
Parquet은 이 경로에서 강력합니다.
ANN 결과에는 좁은 row-level lookup이 필요함
ANN 검색이 후보 row ID를 반환한 후, 시스템은 종종 다음과 같은 필드를 가져와야 합니다:
caption
embedding
rerank feature
video_uri
metadata
이 패턴은 더 적은 행, 보통 수백 또는 수천 개를 읽지만, row ID별로 정확한 접근이 필요합니다. 특정 행과 열을 찾아 필요한 byte range만 가져오고, 몇 개의 레코드를 검색하기 위해 전체 row group을 가져오는 일을 피하고자 합니다.
Point lookup은 scanning과 거의 반대되는 선호를 가집니다. 더 작은 read granularity를 원합니다. 이상적으로는 storage layer가 row ID로 관련 segment 또는 byte range를 찾고, 해당 range만 읽고, 결과에 필요한 데이터만 decode할 수 있어야 합니다.
압축도 다른 tradeoff를 가집니다. scan에서는 시스템이 많은 데이터를 읽고 I/O를 절약하기 때문에 더 강한 압축이 종종 가치가 있습니다. point lookup에서는 한 행을 가져오기 위해 훨씬 더 큰 compressed block을 decode해야 한다면 압축이 부담이 될 수 있습니다.
하나의 레이아웃으로 두 경로를 모두 최적화할 수 없음
이것이 핵심 충돌이다. 스칼라 필터링과 분석은 넓고, 압축되어 있으며, 스캔에 친화적인 레이아웃을 원한다. 벡터 조회는 좁고, 정밀하며, 행 주소 지정이 가능한 레이아웃을 원한다.
하나의 파일 형식이 어느 정도 둘 다 지원할 수는 있지만, 동시에 둘 모두에 최적일 수는 없다.
모든 컬럼이 Parquet에 있다면 스칼라 스캔은 편하다. 하지만 리콜 이후의 ANN 조회는 더 어려워진다. 시스템은 몇백 개의 벡터, 캡션 또는 메타데이터 레코드만 필요할 수 있지만, 스토리지 계층은 대부분 무관한 행을 포함하는 큰 row group을 읽어야 할 수 있다.
로컬 SSD에서는 캐시와 mmap이 이 비용의 일부를 숨길 수 있다. 데이터가 object storage에 저장되면 이 비용은 더 뚜렷해진다. 모든 캐시 미스가 원격 range read가 될 수 있다. 후보 행이 여러 row group에 흩어져 있으면, 단일 쿼리가 여러 번의 read를 트리거할 수 있으며, 각각은 쿼리에 필요한 것보다 더 많은 데이터를 가져온다. 잘못 배치된 레이아웃에서는 후보 행 1,000개를 가져오는 일이 쉽게 수십 또는 수백 메가바이트의 불필요한 I/O로 이어질 수 있고, 극단적인 경우에는 훨씬 더 커질 수 있다.
row group을 더 작게 만들면 point lookup에는 도움이 되지만, 스캔에는 해롭다. 너무 많은 작은 fragment는 압축 효율을 낮추고, 메타데이터 오버헤드를 증가시키며, 분석 엔진이 의존하는 긴 순차 read를 깨뜨린다.
따라서 문제는 하나의 마법 같은 row group 크기를 찾는 것이 아니다. 문제는 동일한 dataset이 두 가지 다른 storage system처럼 동작하라는 요구를 받고 있다는 것이다.
Hybrid search는 두 경로를 하나의 쿼리로 강제한다
Hybrid search는 이 충돌을 무시하기 어렵게 만든다. 단일 쿼리는 먼저 스칼라 필터를 적용할 수 있다:
aesthetic_score > 0.8 AND duration > 5
그런 다음 ANN search를 실행한다.
그런 다음 row ID로 caption, vector, metadata를 가져온다.
사용자에게 이것은 하나의 search request다. 스토리지 계층에는 이것이 분석 스캔이면서 동시에 저지연 random lookup이다.
그렇기 때문에 vector storage에는 더 나은 Parquet 설정 이상의 것이 필요하다. 실제로 어떻게 읽히는지에 따라 서로 다른 컬럼을 배치할 수 있는 방법이 필요하다.
세 번째 문제: dataset은 하나의 엔진 안에 존재하지 않는다
처음 두 문제는 데이터베이스 내부에서 발생한다. 세 번째는 시스템 간 경계에서 발생한다.
AI data pipeline은 여러 시스템에 걸쳐 있다
비디오 워크플로에서는 vector database 자체 안에서 일어나는 일이 거의 없다.
원본 비디오는 object storage에 있다. Clip generation은 Spark 또는 Ray에서 실행될 수 있다. Aesthetic scoring은 GPU service에서 실행될 수 있다. Captioning은 LLM inference pipeline에서 실행될 수 있다. Embedding은 또 다른 GPU job에서 생성될 수 있다. Sparse vector는 SPLADE service에서 올 수 있다. Offline evaluation, training data filtering, human review, governance job은 모두 다른 곳에서 실행될 수 있다.
vector database는 online search를 제공하지만, dataset은 여러 시스템에 의해 생성되고, 수정되고, 평가되고, 확장된다.
Private storage format은 여러 개의 진실 사본을 만든다
데이터베이스가 자기만 읽고 쓸 수 있는 private physical format을 사용한다면, 모든 외부 job에는 export, conversion, copy, import가 필요하다. 동일한 collection이 데이터베이스, Spark temporary directory, evaluation output, local backfill directory에 존재할 수 있다. 그러면 진짜 질문은 다음과 같아진다:
- 어떤 copy가 source of truth인가?
- 어느 것이 지난달의 caption model을 포함하고 있는가?
- 어떤 row가 이미 human review로 수정되었는가?
- 어떤 sparse vector column이 어떤 model로 생성되었는가?
- backfill 이후에도 어떤 vector index가 여전히 유효한가?
- 이 row는 어떤 원본 video object를 참조하는가?
작은 규모에서는 팀이 naming convention과 수동 확인으로 때때로 버틸 수 있다. 수억 개의 row와 테라바이트 규모의 embedding에서는 이것이 consistency problem이 된다.
Vector dataset에는 공유되는 versioned state가 필요하다
레이크하우스 시스템은 구조화된 데이터에 대해 이 문제의 한 버전을 해결했습니다. Iceberg, Delta Lake, Hudi는 단순히 파일을 저장하는 것에 관한 것이 아닙니다. 이들의 핵심 기여는 여러 엔진이 동일한 테이블 상태를 중심으로 조율할 수 있게 하는 것입니다.
벡터 데이터베이스에도 이제 유사한 기능이 필요하지만, 상태는 더 복잡합니다. 여기에는 테이블 파일과 파티션뿐만 아니라 벡터 인덱스, 텍스트 인덱스, 희소 특징, 삭제 로그, 통계, 행 ID 범위, 외부 blob에 대한 참조도 포함되어야 합니다.
질문은 단순히 “Spark가 Milvus 파일을 읽을 수 있는가?”가 아닙니다.
질문은 Spark가 희소 벡터 열을 백필한 후, Milvus가 그 열이 어떤 버전에 속하는지, 어떤 행을 포함하는지, 어떤 모델이 그것을 생성했는지, 그리고 온라인 쿼리가 언제 안전하게 그것을 사용할 수 있는지를 어떻게 아는가입니다.
그 답은 스토리지 모델 안에 있어야 합니다.
패치만으로는 충분하지 않은 이유
이것들을 세 가지 별개의 엔지니어링 문제로 취급하고 싶은 유혹이 있습니다.
- 쓰기 증폭? 배칭을 추가하면 됩니다.
- 포인트 읽기? 캐시를 추가하면 됩니다.
- 외부 시스템? 내보내기 및 가져오기 도구를 추가하면 됩니다.
이러한 패치는 도움이 될 수 있지만, 근본적인 문제를 해결하지는 못합니다. 벡터 데이터셋은 물리적으로 이질적이라는 점입니다.
동영상 예시에서 clip_id, video_id, duration, aesthetic_score는 짧은 스칼라 필드입니다. 이들은 필터링과 분석에 유용합니다.
caption은 텍스트입니다. BM25, 검토, 수정, 백필에 사용될 수 있습니다.embedding은 길고 밀집된 벡터입니다. ANN recall에 사용되며, 이후 행 수준 조회나 재랭킹에도 사용됩니다.embedding_v2는 새로운 모델 출력이며, 원본 데이터가 삽입된 지 한참 후에 백필되는 경우가 많습니다.sparse_vector는 하이브리드 검색을 지원하며 자체적인 접근 패턴을 가집니다.- 원본 동영상은 객체 스토리지에 남아 있어야 합니다. 데이터베이스는 참조, 체크섬, MIME 유형, 파서 버전, 행 수준 관계를 저장해야 합니다.
- 벡터 인덱스, 텍스트 인덱스, 통계, 삭제 로그는 자체적인 버전 의미론을 가진 파생 객체입니다.
이 객체들은 논리적 행을 공유하지만, 모두 동일한 물리적 레이아웃이나 수명주기를 공유해서는 안 됩니다.
- 하나의 일반적인 테이블 레이아웃에 강제로 넣으면 업데이트 비용이 높아집니다.
- 하나의 컬럼형 파일 형식에 강제로 넣으면 포인트 읽기 비용이 높아집니다.
- 서로 관련 없는 객체 파일로 취급하면 버전 관리가 취약해집니다.
따라서 스토리지 모델은 데이터셋이 이질적이라는 사실에서 출발해야 합니다.
이는 세 가지 설계 요구사항으로 이어집니다:
- 첫째, 서로 다른 열 그룹은 서로 다른 물리적 형식으로 저장되어야 합니다.
- 둘째, 이러한 열 그룹은 공유 행 ID 공간을 가져야 하며, 그래야 여전히 하나의 논리적 테이블처럼 동작할 수 있습니다.
- 셋째, 데이터셋에는 현재 뷰에 속하는 파일, 인덱스, 로그, 통계, 객체 참조를 선언하는 버전 관리된 Manifest가 필요합니다.
이것이 Milvus와 Zilliz Cloud를 뒷받침하는 새로운 스토리지 엔진인 Loon의 설계입니다.
Loon: 진화하는 벡터 데이터셋을 위한 Milvus 및 Zilliz Cloud 기반 스토리지 엔진
위의 모든 문제를 해결하기 위해, 우리는 Milvus와 Zilliz Vector Lakebase(Zilliz Cloud의 다음 진화)를 위한 새로운 스토리지 엔진인 Loon을 구축했습니다. 이는 진화하는 벡터 데이터셋을 위해 설계되었습니다.
이 이름은 Zilliz의 새 이름 명명 전통을 따릅니다. loon은 호수에 사는 잠수성 조류로, 시스템의 목표와 잘 맞아떨어집니다. 벡터 데이터베이스는 쿼리를 실행하거나, 열을 백필하거나, 인덱스를 구축할 때마다 데이터의 호수 전체를 이동하거나 스캔하거나 다시 쓸 필요가 없어야 합니다. 먼저 열, 인덱스, 통계, 삭제 로그, 객체 참조를 포함한 현재 데이터셋 버전을 이해한 다음, 실제로 필요한 부분만 읽어야 합니다.
하이브리드 파일 형식, 행 ID 정렬, Manifest는 세 가지 별개의 기능이 아닙니다. 이들은 동일한 설계 가정에서 비롯됩니다. 벡터 데이터셋은 본질적으로 이질적이라는 것입니다.
세 가지 요소, 하나의 스토리지 모델
하이브리드 파일 형식은 서로 다른 컬럼이 서로 다른 접근 패턴을 가진다는 점을 인정합니다. 스칼라 필드는 스캔과 필터에 적합합니다. 벡터 필드는 효율적인 행 수준 조회가 필요합니다. 동영상, PDF, 이미지, 오디오 파일과 같은 원시 객체는 데이터베이스 데이터 파일 내부가 아니라 객체 스토리지에 있어야 합니다.
Row ID 정렬은 이러한 컬럼들이 물리적으로 분리되어 있을 수 있지만, 여전히 동일한 논리적 행을 설명한다는 점을 인정합니다. 캡션, 임베딩, 희소 벡터, 동영상 URI가 서로 다른 파일과 형식에 존재할 수 있지만, 여전히 하나의 결과로 다시 결합되어야 합니다.
Manifest는 데이터셋이 한 번 작성된 뒤 그대로 방치되는 것이 아니라는 점을 인정합니다. 데이터셋은 여러 시스템에 의해, 여러 버전에 걸쳐, 여러 작업을 위해 수정됩니다. 인덱스, 통계, 삭제 로그, 외부 객체 참조, 컬럼 그룹은 모두 동일한 버전 관리 뷰에 나타나야 합니다.
이것이 Loon이 단순히 더 빠른 벡터 파일 형식이 아닌 이유입니다. 더 빠른 형식은 포인트 조회에는 도움이 되지만, 스키마 진화나 다중 엔진 조정을 해결하지는 못합니다. Row ID 정렬은 분리된 컬럼들이 하나의 테이블처럼 동작하게 해주지만, 어떤 파일이 현재 버전에 속하는지는 지정하지 않습니다. Manifest는 데이터셋 상태를 설명할 수 있지만, 컬럼 그룹과 Row ID 정렬이 없으면 하나의 논리적 컬렉션 안에서 서로 다른 물리적 레이아웃을 깔끔하게 표현할 수 없습니다.
스토리지 모델에는 세 가지가 모두 필요합니다. 서로 다른 컬럼 그룹을 위한 서로 다른 형식, 행을 재구성하기 위한 공유 Row ID 공간, 그리고 모든 리더와 라이터에게 현재 데이터셋이 무엇인지 알려주는 버전 관리 Manifest입니다.
Milvus와 Zilliz Vector Lakebase에서 Loon의 위치
Milvus에서 Loon은 기존 세그먼트 binlog 스토리지 계층을 Manifest, ColumnGroup, 파일 형식, 파일시스템 추상화를 중심으로 구축된 모델로 대체합니다. Zilliz Vector Lakebase (Zilliz Cloud의 다음 진화 단계)에서는, 동일한 방향이 Vector Lakebase 아키텍처에도 적용됩니다. 벡터 데이터베이스의 서빙 경로는 빠르게 유지하면서, 기반 데이터는 외부 시스템과 함께 더 쉽게 진화, 분석, 조정할 수 있게 만드는 것입니다.
상위 수준의 Milvus 컴포넌트는 여전히 익숙한 역할을 유지합니다. Proxy는 라우팅을 처리합니다. QueryCoord와 DataCoord는 스케줄링을 처리합니다. IndexNode는 인덱스를 빌드합니다. 컬렉션, 삽입, 검색, 하이브리드 검색을 위한 애플리케이션 대상 API는 Manifest 파일이나 ColumnGroups를 노출할 필요가 없습니다.
변화는 그 아래에 있습니다.
DataNode, QueryNode, segcore, compaction, 외부 커넥터는 동일한 스토리지 추상화를 통해 동작할 수 있습니다. 이것이 중요한 이유는 데이터셋이 더 이상 데이터베이스에 의해서만 쓰이고 읽히지 않기 때문입니다. 외부 컴퓨팅 시스템에 의해 확장되면서 동시에 온라인 검색에 의해 소비될 수 있습니다.
상위 수준에서 계층은 다음과 같습니다:
Manifest
→ ColumnGroup
→ file format layer
→ filesystem abstraction
Manifest는 데이터셋의 버전 관리 상태를 설명합니다. ColumnGroups는 논리적 컬렉션을 물리적 컬럼 그룹으로 매핑합니다. 파일 형식 계층은 각 ColumnGroup이 적절한 형식을 선택할 수 있게 합니다. 파일시스템 추상화는 객체 스토리지와 로컬 스토리지 전반에서 작동합니다.
중요한 점은 하이브리드 파일 형식, Row ID 정렬, Manifest가 별개의 기능이 아니라는 것입니다. 이들은 함께 스토리지 모델을 정의합니다.
이 모델이 마련되면, 세 가지 설계 선택을 하나씩 살펴볼 수 있습니다. Loon이 서로 다른 ColumnGroups를 어떻게 저장하는지, 이를 어떻게 다시 행으로 정렬하는지, 그리고 Manifest가 이러한 파일들을 어떻게 버전 관리되는 데이터셋으로 전환하는지입니다.
설계 1: 올바른 컬럼 그룹에 올바른 파일 형식을 사용하기
서로 다른 컬럼은 서로 다른 접근 패턴을 가집니다. 이들을 동일한 파일 형식에 억지로 넣어서는 안 됩니다.
Loon은 논리적 컬렉션을 ColumnGroup으로 분리합니다.
- 스칼라 필드, 필터 필드, 비즈니스 키, 통계 필드는 종종 스캔, 필터링, 집계되거나 쿼리 계획에 사용됩니다. 이러한 필드는 압축, 컬럼 프루닝, 생태계 호환성의 이점을 얻습니다. Parquet은 이러한 컬럼에 적합합니다.
- 밀집 벡터, 희소 벡터, rerank 기능은 종종 ANN 리콜 이후 행 ID로 읽힙니다. 이들은 낮은 지연 시간의 랜덤 액세스, 정밀한 바이트 범위 읽기, 선택적 디코딩이 필요합니다. 세그먼트 지향 레이아웃이 더 적합합니다. Loon은 이 방향으로 Vortex를 사용합니다.
- 동영상, PDF, 이미지, 오디오 파일과 같은 원시 객체는 벡터 데이터베이스의 데이터 파일에 임베드되어서는 안 됩니다. 객체 스토리지에 그대로 남아 있어야 합니다. 데이터베이스는 참조, 체크섬, MIME 유형, 파서 버전, 행 수준 관계를 기록합니다.
동영상 예시에서 물리적 레이아웃은 다음과 같을 수 있습니다:
Parquet ColumnGroup:
clip_id / video_id / start_offset / duration / aesthetic_score / caption
Vortex ColumnGroups:
embedding
embedding_v2
sparse_vector
Object storage:
raw video objects
애플리케이션 입장에서는 여전히 하나의 컬렉션입니다. 스토리지 계층 입장에서는 해당 컬렉션의 서로 다른 부분이 서로 다른 물리적 형식을 사용합니다. 이는 불필요한 재작성을 직접적으로 줄입니다. embedding_v2 추가는 새로운 벡터 ColumnGroup과 Manifest 커밋이 될 수 있습니다. caption 컬럼, 스칼라 메타데이터, 기존 embedding 컬럼을 재작성할 필요가 없습니다.
동일한 아이디어는 희소 벡터, rerank 기능 또는 기타 파생 필드에도 적용됩니다. 새 컬럼이 물리적으로 독립적이고 행 ID로 정렬될 수 있다면, 관련 없는 컬럼들을 같은 재작성 경로로 끌고 갈 필요가 없습니다.
Loon은 파일 형식 사용 방식도 조정합니다.
Parquet의 경우, 기본 설정이 벡터 중심 데이터에 항상 이상적인 것은 아닙니다. 64 MB row group은 작은 랜덤 읽기가 필요한 것보다 훨씬 더 많은 데이터를 가져올 수 있기 때문에 포인트 조회에는 너무 클 수 있습니다. Loon은 관련 경로에서 row group을 1 MB로 줄이고, 벡터 컬럼에서 dictionary encoding과 같은 인코딩이 랜덤해 보이는 벡터 데이터에 도움이 되지 않을 때는 이를 비활성화합니다.
Vortex의 경우, 더 중요한 작업은 레이아웃입니다. Loon은 스캔 효율성과 포인트 조회의 균형을 맞추는 레이아웃을 사용합니다. row group 내에서는 관련 컬럼의 세그먼트를 서로 가깝게 배치해 스캔을 지원할 수 있습니다. 작업을 수행할 때 sub-segment 읽기는 전체 세그먼트를 가져오는 대신 시스템이 관련 바이트만 가져올 수 있게 합니다.
Loon은 읽기 전용 Lance 통합도 지원하므로, 호환성이 중요할 때 기존 Lance 데이터셋을 ColumnGroup으로 마운트할 수 있습니다.
벤치마크가 보여주는 것
한 로컬 테스트에서, 40,000개 행과 {id: int64, name: utf8, value: float64, vector: list<float32>[128]} 스키마를 가진 단일 파일을 사용했을 때, Vortex는 1 MB row group을 가진 Parquet 대비 다음 결과를 보였습니다:
| 작업 | Vortex | Parquet | 차이 |
|---|---|---|---|
| Take, K=1000 random rows | 5.8 ms | 144 ms | 25배 빠름 |
| Full vector-column scan | 21 ms | 142 ms | 6.76배 빠름 |
| File size, ~21 MB raw data | 6.62 MB | 7.16 MB | 7% 더 작음 |
take 결과는 읽고 디코딩해야 하는 관련 없는 데이터의 양을 줄인 데서 비롯됩니다. 스캔 결과는 압축 및 구현 선택에서 비롯됩니다.
이 수치는 해당 설정에 한정해 이해해야 합니다: 8 vCPU Ubuntu 22.04 KVM, 로컬 파일시스템, 단일 파일, 40,000개 행, 1 MB row group, 그리고 위의 스키마. 객체 스토리지에서는 네트워크 I/O가 지배적일 수 있으므로, 읽기 증폭을 줄이는 것이 훨씬 더 중요할 수 있습니다. 실제 결과는 데이터셋 형태, 객체 스토리지 동작, 캐시 상태, 쿼리 패턴에 따라 달라집니다.
더 넓은 요점은 모든 컬럼이 Vortex를 사용해야 한다는 것이 아닙니다.
요점은 벡터 데이터셋에는 ColumnGroup 수준의 파일 형식 선택이 필요하다는 것입니다.
설계 2: 행 ID를 통해 물리 파일 정렬
하이브리드 파일 형식은 한 가지 문제를 해결합니다. 서로 다른 열이 이제 각자에게 가장 적합한 형식에 저장될 수 있다는 점입니다.
하지만 이는 두 번째 문제를 만들어냅니다. 스칼라 필드가 Parquet에 있고, 벡터가 Vortex에 있으며, 원시 객체가 객체 스토리지에 있다면, 시스템은 어떻게 여전히 그것들을 하나의 컬렉션으로 취급할 수 있을까요?
Loon은 행 ID 정렬로 이 문제를 해결합니다.
행 ID는 스토리지 계층의 좌표계입니다
각 물리적 ColumnGroupFile은 파일 경로와 자신이 포괄하는 행 ID 범위를 기록합니다.
path
start_index
end_index
서로 다른 ColumnGroup은 다른 파일과 형식에 저장되어 있더라도 동일한 행 ID 공간을 포괄할 수 있습니다.
행 ID 12345의 경우, 스칼라 메타데이터는 Parquet ColumnGroup에 있고, 임베딩은 Vortex ColumnGroup에 있으며, 원시 비디오는 객체 스토리지 참조로 표현될 수 있습니다. 논리적으로는 여전히 하나의 행입니다. 이는 스토리지 계층에 안정적인 좌표계를 제공합니다.
행 ID는 비즈니스 기본 키가 아닙니다. 이는 Loon이 컬렉션을 물리적으로 분할하면서도 논리적으로 재구성할 수 있는 능력을 잃지 않게 해주는 스토리지 계층의 좌표계입니다.
새 열은 기존 열을 다시 쓸 필요가 없습니다
embedding_v2를 추가한다고 해서 원래의 캡션, 메타데이터 또는 embedding_v1 ColumnGroup을 다시 쓸 필요는 없습니다. Loon은 새 벡터 ColumnGroup을 작성하고, 그것이 포괄하는 행 ID 범위를 기록한 뒤, Manifest를 통해 그 변경 사항을 커밋할 수 있습니다.
나중에 도착하는 희소 벡터, rerank 기능 또는 기타 파생 필드에도 동일하게 적용됩니다.
새 ColumnGroup이 올바른 행 ID 범위를 포괄하기만 하면, 관련 없는 데이터를 이동시키지 않고도 동일한 논리적 컬렉션에 결합될 수 있습니다.
삭제와 컴팩션은 더 정밀하게 수행될 수 있습니다
행 ID 정렬은 삭제에도 도움이 됩니다.
삭제는 먼저 삭제 로그를 통해 표현될 수 있습니다. 해당 행은 논리적 수준에서 보이지 않게 되며, 물리적 정리는 컴팩션까지 지연됩니다. 컴팩션이 결국 실행될 때, 영향을 받은 행에 연결된 모든 ColumnGroup을 항상 다시 쓸 필요는 없습니다. 정리가 필요한 ColumnGroup에 집중할 수 있습니다.
이는 모든 열의 비용 특성이 동일하지 않기 때문에 중요합니다. 짧은 스칼라 ColumnGroup을 다시 쓰는 것은 수백 기가바이트의 밀집 벡터를 다시 쓰는 것과 매우 다릅니다.
하이브리드 검색은 필요한 열만 가져올 수 있습니다
행 ID 정렬은 하이브리드 파일 형식 위에서 하이브리드 검색을 실용적으로 만드는 요소이기도 합니다.
ANN 검색이 후보 행 ID를 반환한 후, 시스템은 최종 결과에 필요한 필드만 가져올 수 있습니다. 캡션, 메타데이터, 벡터, rerank 기능 또는 객체 참조가 그것입니다.
예를 들어, 쿼리에는 다음이 필요할 수 있습니다.
caption
embedding
video_uri
이러한 필드는 서로 다른 ColumnGroup에 저장되어 있을 수 있습니다. Loon은 행 ID 범위로 관련 파일을 찾고, 필요한 바이트 범위를 읽은 뒤, 결과를 조립할 수 있습니다.
행 ID 정렬이 없다면, 하이브리드 형식은 그저 나란히 놓인 별도의 파일들일 뿐입니다. 행 ID 정렬이 있으면, 그것들은 하나의 논리적 컬렉션처럼 동작합니다.
Packed Reader는 상위 계층에서 분할을 숨깁니다
이를 사용할 수 있게 만드는 런타임 구성 요소는 Packed Reader입니다.
상위 계층은 통합된 Arrow RecordBatch 스트림을 봅니다. 그 아래에서 데이터는 서로 다른 파일 형식의 여러 ColumnGroup에서 올 수 있습니다. Packed Reader는 이러한 차이를 숨기고, 행 ID 범위로 데이터를 정렬하며, 제어된 메모리 사용량으로 다중 파일 I/O를 스케줄링합니다.
또한 행 ID를 통한 직접 take도 지원합니다. 행 ID 집합이 주어지면, 관련 ColumnGroupFiles를 찾고, 범위 읽기를 발행하며, 요청된 필드를 반환합니다.
비디오 워크플로의 경우, ANN 쿼리에는 caption, embedding, video_uri가 필요할 수 있습니다. Packed Reader는 관련 없는 열을 건드리지 않고 스칼라 ColumnGroup과 벡터 ColumnGroup을 가져올 수 있습니다.
그것이 “별도의 파일”과 “여러 물리적 레이아웃을 가진 테이블”의 차이입니다.
설계 3: Manifest를 진실의 원천으로 만들기
하이브리드 파일 형식은 데이터가 물리적으로 저장되는 방식을 정의합니다. Row ID 정렬은 분리된 ColumnGroup들이 여전히 하나의 논리적 테이블을 형성하는 방식을 결정합니다. 하지만 시스템은 여전히 더 큰 질문에 답해야 합니다. 현재 버전의 데이터셋에 어떤 파일, 로그, 통계, 인덱스, 객체 참조가 속하는가? 그것이 Manifest의 역할입니다.
객체 스토리지 디렉터리만으로는 충분하지 않습니다
객체 스토리지는 데이터베이스 카탈로그가 아닙니다. 디렉터리에는 오래된 파일, 새 파일, 실패한 작업 출력, 임시 파일, 삭제 로그, 이전 스냅샷에서 여전히 참조되는 파일, 정리를 기다리는 파일이 포함될 수 있습니다. 파일이 존재한다는 사실이 해당 파일이 현재 데이터셋 버전에 속한다는 뜻은 아닙니다.
Loon 데이터셋은 다음과 같은 디렉터리로 구성될 수 있습니다:
_metadata/
_data/
_delta/
_stats/
_index/
하지만 디렉터리 구조는 진실의 원천이 아닙니다. Manifest가 그렇습니다. 리더는 디렉터리를 나열하고 우연히 존재하는 파일로부터 상태를 추론해서는 안 됩니다. 현재 Manifest를 읽고 그것이 선언하는 버전이 지정된 뷰를 따라야 합니다.
Manifest는 데이터셋의 하나의 버전이 지정된 뷰를 정의합니다
Manifest는 주어진 버전에서 데이터셋을 정의합니다. 여기에는 다음이 기록됩니다:
- 어떤 ColumnGroup이 존재하는지
- 이들이 어떤 row ID 범위를 포함하는지
- 각 ColumnGroup이 어떤 물리적 형식을 사용하는지
- 파일이 어디에 있는지
- 어떤 삭제 로그가 활성 상태인지
- 어떤 통계를 사용할 수 있는지
- 어떤 인덱스가 존재하는지
- 어떤 외부 blob이 참조되는지
- 해당 통계나 인덱스가 어떤 컬럼과 row 범위를 포함하는지
각 업데이트는 새로운 Manifest 버전을 씁니다. 버전 N을 여는 리더는 버전 N에서 데이터셋의 안정적인 뷰를 봅니다. 작성자는 여전히 버전 N을 사용 중인 리더를 방해하지 않고 버전 N+1을 준비할 수 있습니다.
Manifest는 테이블 파일 이상의 것을 추적합니다
Loon에서 Manifest 본문은 Apache Avro로 인코딩되며 네 가지 주요 섹션을 중심으로 구성됩니다.
- ColumnGroup은 컬럼, 형식, 파일, row ID 범위를 설명합니다.
- DeltaLog는 삭제를 설명합니다. 서로 다른 삭제 유형은 클라이언트의 primary-key 삭제, 내부 compaction의 positional 삭제, 외부 엔진의 equality 삭제와 같은 서로 다른 변경 소스를 포함합니다.
- Stats는 bloom filter, BM25 통계, min/max 값과 같은 계획 메타데이터를 포함합니다.
- Index는 인덱스 유형, 매개변수, 포함된 컬럼, row ID 범위를 설명합니다. 여기에는 HNSW나 IVF와 같은 벡터 인덱스, 텍스트 인덱스, inverted index, bitmap index 및 관련 구조가 포함될 수 있습니다.
이 지점에서 Loon은 전통적인 테이블 manifest와 다릅니다.
벡터 데이터셋은 데이터 파일과 파티션만 추적하면 되는 것이 아닙니다. 벡터 인덱스, 텍스트 인덱스, sparse feature, 삭제 로그, 통계, 외부 객체 참조, 그리고 이들을 연결하는 row ID 범위도 추적해야 합니다.
Manifest는 데이터베이스 이외의 주체도 쓸 수 있어야 합니다
가장 중요한 부분은 Manifest에 무엇이 들어 있는지만이 아닙니다. 누가 그것을 쓸 수 있는가입니다.
- 데이터베이스만 Manifest를 쓸 수 있다면, 그것은 내부 메타데이터로 남습니다. 더 깔끔한 메타데이터이긴 하지만, 여전히 하나의 엔진에만 비공개입니다.
- 외부 엔진이 새로운 ColumnGroup, 통계, Manifest 항목을 생성할 수 있다면, Manifest는 조정 인터페이스가 됩니다.
- 예를 들어 Spark 작업은 sparse vector 컬럼을 backfill할 수 있습니다. 새로운 ColumnGroup을 쓰고, row 포함 범위와 통계를 기록한 뒤, 새로운 Manifest를 커밋합니다. 온라인 쿼리는 작업 중에도 기존 버전을 계속 읽을 수 있습니다. 커밋이 성공하면 새 버전이 표시됩니다.
이는 정신적으로 Iceberg 및 Delta Lake와 유사하지만, 객체 모델은 더 넓습니다. 벡터 데이터셋은 단순히 테이블 파일과 파티션뿐만 아니라 벡터 인덱스, 텍스트 인덱스, sparse feature, 삭제 로그, 통계, blob 참조, row ID 범위를 추적해야 합니다.
낙관적 커밋은 버전 업데이트를 단순하게 유지합니다
각 커밋은 새로운 Manifest 버전을 작성합니다. 작성자는 버전 N을 기반으로 새 콘텐츠를 빌드한 다음 manifest-{N+1}.avro 쓰기를 시도할 수 있습니다. 객체 스토리지의 조건부 쓰기 또는 generation-match 시맨틱은 해당 버전이 이미 존재하는 경우 커밋이 실패하도록 만들 수 있습니다. 그러면 작성자는 더 새로운 버전에 대해 다시 시도할 수 있습니다.
이를 통해 Loon은 모든 업데이트를 무겁고 강력하게 일관된 조정 경로로 강제하지 않고도 낙관적 동시성을 제공합니다. Manifest가 없으면 멀티 포맷 및 멀티 엔진 스토리지는 결국 명명 규칙과 수동 조정으로 변하게 됩니다. 이는 작은 데이터셋에는 작동할 수 있습니다. TB 규모의 벡터 데이터에는 작동하지 않습니다.
Manifest는 이기종 파일을 여러 시스템이 안전하게 읽고 업데이트할 수 있는 데이터셋으로 바꾸는 요소입니다.
스토리지가 버전 관리될 때 사용자를 위해 바뀌는 점
애플리케이션 개발자에게 Loon이 새로운 API 부담이 되어서는 안 됩니다.
사용자는 여전히 익숙한 Milvus 개념인 collection, insert, search, hybrid search로 작업할 수 있어야 합니다. 일반적인 애플리케이션 개발 중에 Manifest 파일, ColumnGroup, 행 ID 범위 또는 파일 레이아웃에 대해 생각할 필요가 없어야 합니다.
변화는 내부에 있습니다. 스토리지는 AI 데이터셋이 실제로 어떻게 진화하는지 더 잘 인식하게 됩니다.
새 임베딩을 추가한다고 기존 데이터를 이동해서는 안 됩니다
이전에는 기존 collection에 embedding_v2를 추가하려면 데이터를 내보내고, 새 모델을 학습시키고, 벡터를 생성한 다음 SDK를 통해 collection을 다시 가져오거나 대량 업데이트해야 하는 경우가 많았습니다. 이 경로는 버전 추적, 실패한 작업 재시도, 인덱스 재빌드, 서비스 영향, 일관성 검사 등 많은 운영 작업을 만들어냅니다.
Loon을 사용하면 이는 스키마 진화와 새 ColumnGroup 커밋이 될 수 있습니다. 새 임베딩 컬럼은 행 ID에 맞춰 자체 물리적 ColumnGroup으로 작성되고 Manifest를 통해 표시될 수 있습니다. 기존 캡션 컬럼, 스칼라 메타데이터 컬럼, 원래 임베딩 컬럼은 이동할 필요가 없습니다.
백필은 클라이언트 측 업데이트 루프를 요구해서는 안 됩니다
많은 AI 데이터 업데이트는 백필입니다. 팀은 hybrid search가 중요해진 후 sparse vector를 추가할 수 있습니다. 새 모델이 학습된 후 rerank feature를 추가할 수 있습니다. 사람의 검토 후 캡션을 수정할 수 있습니다. 정책 업데이트 후 거버넌스 태그를 추가할 수 있습니다.
전통적인 레이아웃에서는 데이터가 Spark, Ray 또는 다른 외부 엔진에서 생성되더라도 이러한 변경이 클라이언트 SDK 업데이트나 데이터베이스 전용 쓰기 경로를 통해 발생하는 경우가 많습니다.
Loon을 사용하면 외부 컴퓨트 시스템이 새 ColumnGroup을 생성하고 Manifest를 통해 커밋할 수 있습니다. 더 이상 데이터베이스가 모든 재작성의 유일한 진입점일 필요가 없습니다.
오프라인 분석은 진실의 또 다른 복사본을 요구해서는 안 됩니다
이전에는 팀이 오프라인 평가나 분석을 위해 온라인 collection을 Parquet로 덤프하는 경우가 많았습니다. 이는 동일한 데이터셋의 두 가지 버전, 즉 온라인 collection과 분석 복사본을 만듭니다. 캡션이 수정되고, 임베딩이 재생성되고, 삭제 로그가 적용되거나, 인덱스가 재빌드되면 팀은 어떤 복사본이 최신인지 물어야 합니다.
Manifest 기반 스토리지 모델을 사용하면 분석 엔진이 서빙 시스템과 동일한 버전 관리된 데이터셋 뷰를 읽을 수 있습니다. 필요한 컬럼만 프로젝션하고, 관련 행 범위만 스캔하며, 수동으로 내보낸 스냅샷 대신 선언된 데이터셋 버전을 기준으로 작업할 수 있습니다.
삭제와 수정은 변경된 것만 건드려야 합니다
삭제, 캡션 수정, 레이블 수정, 거버넌스 업데이트는 AI 데이터셋에서 일상적인 작업입니다. 이러한 작업이 모든 긴 벡터 컬럼을 동일한 재작성 경로로 강제해서는 안 됩니다.
Loon을 사용하면 삭제 로그를 먼저 논리적 삭제로 처리할 수 있습니다. 이후 compaction은 관련 없는 데이터를 재작성하지 않고 영향을 받은 ColumnGroup을 정리할 수 있습니다. 짧은 텍스트 필드가 변경되었다고 해서 동일한 논리적 행을 공유한다는 이유만으로 스토리지 계층이 수백 기가바이트의 dense vector를 재작성해야 해서는 안 됩니다.
외부 엔진은 탈출구가 아니라 워크플로의 일부가 됩니다
더 큰 변화는 외부 엔진이 더 이상 벡터 데이터베이스 밖에 있는 시스템으로 취급되지 않는다는 점입니다.
Spark, Ray, 평가 작업, 라벨링 시스템, 거버넌스 파이프라인은 이미 데이터의 상당 부분을 생성하고 수정합니다. 스토리지 계층은 이들이 끊임없이 내보내고, 복사하고, 다시 가져오는 대신 단일 진실 공급원을 중심으로 협업할 수 있게 해야 합니다.
이것이 Manifest의 한 버전이 가능하게 하는 일입니다. 온라인 서빙, 오프라인 분석, 백필 작업, 컴팩션이 데이터셋에 대한 공유된 뷰를 갖게 합니다.
이것들은 내부 스토리지 세부 사항처럼 들릴 수 있지만, 팀이 AI 데이터셋을 얼마나 빠르게 반복 개선할 수 있는지에 영향을 줍니다. 모든 모델 변경, 피처 백필, 캡션 수정, 품질 필터, 인덱스 재구축은 모두 같은 질문에 달려 있습니다: "시스템이 이동할 필요가 없는 데이터를 이동하지 않고 데이터셋을 업데이트할 수 있는가? "
이것이 스토리지 모델의 실질적인 가치입니다.
Loon은 Milvus 3.0 beta 및 Zilliz Vector Lakebase에서 사용할 수 있습니다
Loon은 Milvus 3.0 beta에서 사용할 수 있으며, Zilliz Cloud의 다음 진화인 Zilliz Vector Lakebase의 스토리지 계층에도 포함되어 있습니다. 그리고 이번 릴리스는 세 가지 핵심 영역에 집중합니다:
- Manifest. 목표는 쓰기, 백필, 삭제, 통계, 인덱스 업데이트가 독자가 일관되게 열 수 있는 버전 관리된 데이터셋 뷰를 생성하도록 하는 것입니다. 독자에게 이는 쿼리가 특정 Manifest 버전을 열고 데이터셋의 안정적인 뷰를 볼 수 있음을 의미합니다. 작성자에게 이는 새 데이터 파일, 삭제 로그, 통계 또는 인덱스 파일을 먼저 준비한 뒤 버전 관리된 커밋을 통해 표시할 수 있음을 의미합니다.
- ColumnGroup 및 형식 지원. Parquet는 스칼라 및 생태계 친화적인 컬럼을 지원합니다. Vortex는 벡터 중심 액세스 패턴을 지원합니다. Lance는 기존 Lance 데이터셋과의 호환성을 위해 읽기 전용 모드로 통합될 수 있습니다.
- Lake의 Index. 스칼라 통계, 필터링 인덱스, 텍스트 inverted index는 행 범위별 Manifest 기반 계획에 참여할 수 있습니다. 레이크 네이티브 벡터 인덱스는 더 복잡합니다. HNSW와 IVF는 객체 스토리지에서 서로 다른 동작을 보이며, 특히 HNSW는 랜덤 액세스와 캐시 지역성에 민감합니다. 로컬 SSD용으로 설계된 레이아웃을 그대로 재사용하고 같은 결과를 기대할 수는 없습니다.
아직 해야 할 일이 남아 있습니다
- 외부 쓰기 경로가 중요한 이유는 Spark와 Ray가 모든 백필을 클라이언트 SDK 루프로 강제하지 않고도 ColumnGroup과 Manifest 커밋을 생성할 수 있어야 하기 때문입니다.
- Lakehouse 상호 운용성이 중요한 이유는 많은 팀이 이미 Iceberg, Delta Lake, Trino, DuckDB, Athena와 같은 카탈로그 및 쿼리 엔진을 사용하고 있기 때문입니다. 벡터 데이터는 벡터 검색 성능을 잃지 않고 해당 생태계에 참여할 수 있어야 합니다.
- 인덱스 레이아웃이 중요한 이유는 그래프 인덱스와 inverted 구조가 객체 스토리지에서 서로 다른 액세스 패턴을 갖기 때문입니다.
- 대형 객체 의미론이 중요한 이유는 원본 동영상, PDF, 이미지, 오디오 파일이 파생된 벡터 데이터셋과 정렬되는 참조 관리, 버전 관리, 삭제 동작을 필요로 하기 때문입니다.
정확한 릴리스 동작, 기본 설정, 마이그레이션 경로는 관련 Milvus 및 Zilliz Cloud 릴리스 노트를 따라야 합니다. 그러나 스토리지의 방향은 명확합니다: 벡터 데이터베이스에는 서빙 계층 아래에 버전 관리되는 레이크 네이티브 기반이 필요합니다.
Zilliz Vector Lakebase에서 Loon을 사용해 보세요
현재 스택이 온라인 서빙, 오프라인 분석, 백필, 외부 데이터 레이크 워크플로를 서로 다른 시스템으로 분리하고 있다면 Zilliz Vector Lakebase를 살펴볼 만합니다. Zilliz Cloud에서 사용해 볼 수 있습니다. 업무용 이메일로 새로 가입하면 $100 무료 크레딧을 받을 수 있습니다. 사용 사례에 대해 문의하셔도 좋습니다.
오픈 소스 엔진에서 Loon이 어떻게 발전하는지 보려면 Milvus 3.0 릴리스를 팔로우할 수도 있습니다.
Zilliz Vector Lakebase는 다음을 결합합니다:
- 다양한 실시간 성능 및 비용 절충을 위한 계층형 서빙
- 상시 가동 컴퓨팅 없이 대규모 또는 탐색적 워크로드를 위한 온디맨드 검색
- 외부 데이터 레이크 검색을 통해 기존 레이크 데이터에서 직접 인덱싱하고 검색 가능
- 벡터, 텍스트, JSON 및 지리공간 데이터 전반의 풀 스펙트럼 검색, 하이브리드 검색 및 재순위 지정 지원
- 벡터 중심 데이터에 대해 더 빠르고 저렴한 랜덤 읽기를 위해 설계된 개방형 형식인 Vortex 기반의 통합 레이크 네이티브 스토리지
계속 읽기

3 Easiest Ways to Use Claude Code on Your Mobile Phone
Run Claude Code from your phone with Remote Control, Happy Coder, or SSH + Tailscale. Comparison table, setup steps, and tools for typing, memory, and parallel tasks.

Data Deduplication at Trillion Scale: How to Solve the Biggest Bottleneck of LLM Training
Explore how MinHash LSH and Milvus handle data deduplication at the trillion-scale level, solving key bottlenecks in LLM training for improved AI model performance.

Zilliz Cloud Enterprise Vector Search Powers High-Performance AI on AWS
Zilliz Cloud on AWS powers secure, scalable, ultra-fast vector search for enterprise AI apps, with BYOC, sub-10ms latency, and zero-DevOps simplicity.


