Hacker News

RIP, vector database

벡터 데이터베이스여, 안녕 — turbopuffer v3의 저장 구조 개편

turbopuffer는 문서와 인덱스를 ANN 주소에 묶던 구조를 바꾸고, ANN을 보조 인덱스로 내리는 v3를 개발하고 있습니다. 문서 중복 저장과 쓰기 증폭을 줄이고 집계 같은 비벡터 쿼리의 성능을 높이는 설계입니다.

AI 요약

turbopuffer는 벡터 검색용 서버리스 데이터베이스로 출발했지만, 텍스트 검색과 정규식 검색, 집계 등으로 쓰임새를 넓혔습니다. v1부터 이어진 저장 구조에서는 ANN(Approximate Nearest Neighbor) 인덱스가 기본 인덱스였습니다. 문서 데이터와 다른 인덱스가 벡터의 ANN 주소를 기준으로 연결됐습니다. 회사는 이 구조가 비벡터 쿼리의 성능과 쓰기 처리량을 제한한다고 보고, v3에서 기본 인덱스를 바꾸려 합니다.

v1에서 v2까지

초기 turbopuffer는 문서마다 ID와 벡터만 저장했습니다. 객체 스토리지와 잘 맞는 계층형 클러스터링 인덱스인 SPANN으로 시작했고, 점진적 인덱싱을 지원하려고 SPFresh로 옮겼습니다. 벡터를 클러스터에 모으고, 각 클러스터의 중심점을 다시 묶어 트리 형태로 구성했습니다. 각 클러스터에는 ClusterId를, 클러스터 안 벡터에는 LocalId를 부여했습니다. 둘을 합친 ANN 주소가 저장소의 키였습니다.

고객이 속성 필터와 전문 검색을 요청하면서 구조가 복잡해졌습니다. 속성 인덱스는 속성값에서 해당 문서의 ANN 주소를 가리켰습니다. BM25 전문 검색 인덱스는 검색어별 문서 목록과 함께 단어 출현 횟수, 문서 길이를 저장했습니다. 정규식 검색, 퍼지 매칭, 희소 벡터 검색, 집계 등도 추가됐지만 문서 저장의 기준은 여전히 ANN 주소였습니다.

벡터 기본 인덱스가 만든 비용

첫 번째 문제는 저장 증폭입니다. 문서 하나에 벡터가 여러 개 있으면, ANN 주소마다 전체 문서 내용을 반복 저장해야 합니다. 문서 중첩이나 late interaction처럼 다중 벡터 표현을 쓰면 중복이 커지고, 제품의 일부 제한으로 이어집니다.

두 번째는 쓰기 증폭입니다. 문서를 추가하거나 수정할 때 SPFresh가 벡터를 재배치하면, 문서의 속성 데이터와 이를 참조하는 역색인도 함께 이동합니다. 벡터 하나를 수정해도 속성과 인덱스 수백 개를 옮길 수 있어 인덱싱 처리량 개선이 점점 어려워졌습니다.

세 번째는 벡터화에 적합한 묶음 크기를 쓰지 못한다는 점입니다. 현대 쿼리 엔진은 데이터를 블록 단위로 처리해 블록별 고정 비용을 나누고, 압축과 SIMD 활용을 개선합니다. DuckDB는 2,048행 단위, ClickHouse는 최대 약 6만5천 행, Lucene은 256개 문서 단위의 포스팅 블록을 씁니다. 반면 turbopuffer의 ANN 클러스터는 100~200개 문서 규모가 적합해, 문서 자체를 읽는 집계 쿼리도 그 크기에 묶였습니다.

전문 검색 인덱스는 이 제약을 피해 별도 블록으로 재구성한 사례입니다. 초기에는 포스팅 목록을 ANN 클러스터 경계에 맞춰 나눴고, 중앙값 기준 블록에 포스팅이 약 1.5개뿐이었습니다. v2에서 포스팅을 약 256개 단위 고정 블록으로 바꾸자 인덱스 크기는 10분의 1로 줄었고 쿼리는 최대 20배 빨라졌습니다. 포스팅이 문서를 가리키는 별도 구조라 클러스터 크기에 맞출 필요가 없었던 덕분입니다. 집계처럼 문서를 직접 훑는 쿼리는 아직 같은 이점을 얻지 못합니다.

v3의 설계와 진행 상황

v3의 변화는 저장 키를 ANN 주소에서 분리하는 것입니다. ANN은 여러 인덱스 가운데 하나인 보조 인덱스가 됩니다. 글은 새 기본 인덱스의 상세 구현을 설명하지는 않지만, 댓글에서 내부적으로 생성한 ID인 (segment ID, doc ID)를 기본 키로 삼는다고 밝혔습니다. 사용자가 지정한 문서 ID는 저장 계층에서 보조 인덱스가 됩니다.

turbopuffer는 이 구조가 텍스트·정규식·벡터 검색을 포함한 모든 쿼리 계획의 성능 개선 기반이 되고, 더 많은 SQL 쿼리를 빠르게 처리하는 데 도움이 될 것으로 설명합니다. 다만 기존 ANN 성능을 떨어뜨리지 않는 일이 과제입니다. 기존 구조로 단일 인덱스 1,000억 개 이상 벡터, p99 읽기 지연 200ms, 초당 1,000건 이상의 쿼리를 처리했다고 밝혔습니다. v3는 CI 테스트를 모두 통과한 단계이며, 다음으로 정확성과 성능을 다듬고 공개 벤치마크를 공유한 뒤 운영 환경에 배포할 계획입니다.

Hacker News 반응

  • @sreekanth850 — 기업용 검색에서 순수 벡터 데이터베이스를 쓸 이유가 많지 않다고 봅니다. 저희는 네이티브 벡터 기능을 갖춘 SQL 데이터베이스 위에 기업 검색 엔진을 만들었습니다. 벡터 유사도 검색은 전문 검색, 필터, 조인, 정렬, 일반 관계형 조건과 나란히 쓰이는 쿼리 연산 하나일 뿐입니다. 테넌트와 앱, 컬렉션 격리도 쿼리에 포함됩니다. ACL, 문서 버전, 카테고리, 메타데이터 조건, 시간 필터를 일반 조건으로 처리합니다. 기업 시스템에는 어차피 SQL이 들어갑니다. 별도 벡터 데이터베이스를 추가하면 시스템이 하나 더 생기고, 데이터 변경 때 두 시스템을 동기화하는 일이 가장 까다롭습니다.
    • @ijidak — 어떤 데이터베이스를 쓰셨나요?
    • @sreekanth850 — CrateDB를 씁니다. ClickHouse와 StarRocks도 이제 벡터 기능을 지원합니다.
  • @blakeashleyjr — 이 글은 10년 뒤의 Postgres 대 InnoDB 논쟁처럼 들립니다. 포스팅이 문서의 물리 위치인 ANN 슬롯을 가리키면 SPFresh가 재조정할 때마다 그 문서에 연결된 모든 인덱스를 다시 씁니다. InnoDB는 보조 인덱스가 기본 키를 가리키게 해 이 문제를 해결하고, 읽을 때 추가 조회 비용을 감수했습니다. S3 GET이 B-tree 이동을 대신하면 그 추가 조회 비용은 얼마나 될까요? 벡터 검색은 결과를 가져올 때만 간접 조회를 쓰도록 클러스터에 벡터 복사본을 둘지 궁금합니다. 그렇지 않으면 콜드 p99가 나빠질 수 있습니다.
    • @alfiedotwtf — 글을 아직 읽진 않았지만 “인덱스가 행이 있는 위치를 가리키지 않게 한다”는 말이 흥미롭습니다. 그렇다면 인덱스는 무엇을 가리키나요?
    • @ddorian43 — 거의 바뀌지 않는 전체 기본 키를 가리킵니다.
  • @drewlanenga — 문서에 벡터가 여러 개 있으면 속성을 벡터마다 복사한다는 문제는 이해됩니다. 금방 커지겠네요. 새 기본 인덱스는 무엇인가요?
    • @benesch — 자동 생성하는 내부 ID인 (segment ID, doc ID)입니다. 문서에서 사용자가 지정하는 id 필드는 저장 계층의 보조 인덱스가 됩니다.
  • @gk1 — 벡터 데이터베이스는 원래 벡터나 데이터 저장보다 검색에 가까웠습니다. 용어가 너무 잘 붙어 회사들이 조금 오래 붙잡고 있었던 것 같네요.
    • @tveita — 그건 그냥 검색 엔진입니다. 이제 Elasticsearch와 Vespa 같은 기존 업체도 벡터 기능을 기본 제공하므로 가격, 성능, 기능으로 경쟁해야 합니다. 웹사이트에서 ‘AI’를 몇 번 언급하는지도 경쟁 항목이겠네요.
  • @Tsarp — 비슷한 용도라면 LanceDB도 좋았습니다. 오픈소스라서만은 아닙니다. Lance는 turbopuffer v3처럼 ANN을 보조 인덱스로 다룹니다. 행은 fragment에 놓고 벡터 인덱스가 행을 이동시키지 않습니다.
  • @ironqcold — 같은 규모에서 초당 1,000건 이상일 때 p99도 보고 싶습니다.
  • @croemer — 대시보드는 9월 5일에 시작한 뒤 9월 7일에 마지막으로 갱신됐습니다. 버그인가요, 진행이 없는 건가요? 글에 링크한 만큼 작동할 거라 기대했습니다.
    • @benesch — 작동합니다. 곧 진행 상황을 확인해 주세요. 성능 최적화를 설명하는 개발 로그가 나오면 그래프도 갱신됩니다. 게시 일정이 몇 주 밀렸습니다. 성능 작업에서 손을 떼고 개발 로그를 쓰기가 어렵네요.

원문: turbopuffer / 번역·요약: Trawling