벡터 인덱스

벡터 인덱스 (Vector Index)

근사 최근접 이웃(ANN) 검색, 반경 검색, 필터 인지 ANN 조회를 위한 벡터 인덱스(HNSW, IVF_FLAT, IVF_PQ, IVF_ON_DISK)를 구성해요.

출처: 문서

본문

Apache Pinot는 임베딩 컬럼에서 효율적인 근사 최근접 이웃(ANN) 검색을 위한 벡터 인덱스를 지원해요. 이 문서는 모든 지원 인덱스 유형, 구성 옵션, 양자화기, 쿼리 패턴, 런타임 튜닝을 다룹니다.

개요

벡터 인덱스는 벡터 공간을 클러스터나 그래프로 분할해 모든 벡터를 스캔하는 대신 하위 선형(sub-linear) 조회를 가능하게 함으로써 유사도 검색을 가속화해요. Pinot는 네 가지 벡터 인덱스 유형을 지원해요.

  • HNSW (Hierarchical Navigable Small World): 그래프 기반, 탁월한 정확도, 적당한 메모리
  • IVF_FLAT: 평면 양자화와 함께하는 Inverted File, 빠른 인덱스 구축
  • IVF_PQ: Product Quantization과 함께하는 Inverted File, 속도/메모리 균형
  • IVF_ON_DISK: 디스크 기반 Inverted File, 2GB JVM 제한 없이 무제한 확장

인덱스 구성

벡터 인덱스는 raw 인코딩을 사용해 테이블의 필드 레벨 indexes 섹션에 구성돼요.

최소 HNSW 구성

{
  "fieldConfigList": [
    {
      "name": "embedding",
      "encodingType": "RAW",
      "indexes": {
        "vector": {
          "vectorIndexType": "HNSW",
          "vectorDimension": 512,
          "vectorDistanceFunction": "COSINE",
          "version": 1
        }
      }
    }
  ]
}

튜닝이 있는 전체 HNSW 구성

{
  "fieldConfigList": [
    {
      "name": "embedding",
      "encodingType": "RAW",
      "indexes": {
        "vector": {
          "vectorIndexType": "HNSW",
          "vectorDimension": 1536,
          "vectorDistanceFunction": "COSINE",
          "version": 1,
          "properties": {
            "maxCon": "16",
            "beamWidth": "200",
            "storeInSegmentFile": "true",
            "refreshMinIntervalMs": "1",
            "refreshWaitTimeoutMs": "5000"
          }
        }
      }
    }
  ]
}

IVF_FLAT 구성

{
  "fieldConfigList": [
    {
      "name": "embedding",
      "encodingType": "RAW",
      "indexType": "VECTOR",
      "properties": {
        "vectorIndexType": "IVF_FLAT",
        "vectorDimension": 768,
        "vectorDistanceFunction": "EUCLIDEAN",
        "version": 1,
        "nlist": "128",
        "trainSampleSize": "20000",
        "quantizer": "SQ8"
      }
    }
  ]
}

IVF_PQ 구성

{
  "fieldConfigList": [
    {
      "name": "embedding",
      "encodingType": "RAW",
      "indexType": "VECTOR",
      "properties": {
        "vectorIndexType": "IVF_PQ",
        "vectorDimension": 768,
        "vectorDistanceFunction": "EUCLIDEAN",
        "version": 1,
        "nlist": "256",
        "trainSampleSize": "50000",
        "pqM": "32",
        "pqNbits": "8",
        "quantizer": "PQ"
      }
    }
  ]
}

IVF_ON_DISK 구성

큰 인덱스를 위한 디스크 기반 IVF. 모든 양자화기 유형과 전체 필터 인지 ANN을 지원해요.

{
  "fieldConfigList": [
    {
      "name": "embedding",
      "encodingType": "RAW",
      "indexType": "VECTOR",
      "properties": {
        "vectorIndexType": "IVF_ON_DISK",
        "vectorDimension": 768,
        "vectorDistanceFunction": "EUCLIDEAN",
        "version": 1,
        "nlist": "256",
        "trainSampleSize": "50000",
        "quantizer": "SQ4"
      }
    }
  ]
}

벡터 인덱스를 columns.psf에 저장

V3 세그먼트에서 벡터 인덱스 구성 properties 맵에 storeInSegmentFile을 설정해 벡터 인덱스 페이로드를 결합된 인덱스 파일(columns.psf)에 저장하고 옆의 백엔드 전용 파일에 두지 않도록 해요. 기본값은 false예요. Pinot는 HNSW, IVF_FLAT, IVF_PQ, IVF_ON_DISK에 대해 이를 지원해요.

  • 플래그가 false에서 true로 바뀌면 Pinot는 다음 세그먼트 로드 시 기존 벡터 인덱스를 columns.psf에 흡수해요.
  • 플래그가 true에서 false로 바뀌면 Pinot는 다음 세그먼트 로드 시 벡터 인덱스를 레거시 온디스크 레이아웃으로 다시 추출해요.
  • storeInSegmentFile이 true일 때 Pinot는 세그먼트 디렉토리가 비로컬이거나 S3의 계층화 저장소처럼 원격 지원일 때도 columns.psf에서 벡터 인덱스를 직접 로드할 수 있어서 로컬 레거시 사이드카 파일이 필요 없어요.
  • 쿼리 표면은 변하지 않아요. 이 플래그는 Pinot가 세그먼트 인덱스 바이트를 저장하는 방식만 바꿔요.

HNSW 스타일 구성에서는 indexes.vector.properties 아래에 속성을 추가하세요. IVF 스타일 구성에서는 벡터 인덱스 properties 맵에 추가하세요.

거리 함수

Function Use Case Range
COSINE 정규화된 텍스트 임베딩 (OpenAI, BERT) [0, 2]
EUCLIDEAN 비정규화 임베딩 또는 기하 데이터 [0, ∞)
DOT_PRODUCT 사전 정규화, 점수가 높을수록 더 유사 (-∞, ∞)
L2 EUCLIDEAN의 별칭 [0, ∞)

양자화기 (Quantizers)

Pinot는 메모리 소비를 검색 속도와 교환하기 위한 범용 양자화기 프레임워크를 지원해요. 양자화기는 IVF 계열 인덱스(IVF_FLAT, IVF_PQ, IVF_ON_DISK)에 적용돼요.

Quantizer Memory per dimension Speed Use Case
FLAT 4 bytes Fastest 높은 메모리 예산, 최대 정확도
SQ8 1 byte Fast 8비트 스칼라 양자화
SQ4 0.5 bytes Very fast 4비트 스칼라 양자화, 최대 압축
PQ Variable Medium product quantization이 있는 대규모

SQ8과 SQ4는 IVF 생성기, 판독기, 검색 경로를 통해 완전히 통합돼 있어요. 이들은 검증 전용 기능이 아니라 실제 백엔드 기능이에요.

SQL 함수

VECTOR_SIMILARITY — Top-K ANN 검색

구성된 벡터 인덱스를 사용해 k개의 최근접 이웃을 반환해요.

SELECT ProductId,
       cosineDistance(embedding, ARRAY[0.12, 0.34, 0.56, ...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY(embedding, ARRAY[0.12, 0.34, 0.56, ...], 10)
ORDER BY dist ASC
LIMIT 10;

VECTOR_SIMILARITY_RADIUS — 거리 기반 검색

고정 top-K 없이 거리 임계값 안의 모든 벡터를 반환해요.

SELECT ProductId,
       cosineDistance(embedding, ARRAY[0.12, 0.34, 0.56, ...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY_RADIUS(embedding, ARRAY[0.12, 0.34, 0.56, ...], 0.3)
ORDER BY dist ASC;

벡터 인덱스가 없는 세그먼트에서는 자동으로 전수 스캔(brute-force scan)으로 폴백해요. 근사 반경 지원은 실제 인덱스 지원 반경 검색이 제공되는 백엔드에서만 광고돼요.

필터 인지 ANN (Filter-Aware ANN)

쿼리가 벡터 조건자를 메타데이터 필터와 결합할 때 Pinot는 벡터 후보 생성을 일치하는 행 ID로 제한할 수 있어요. 이는 먼저 ANN 후보를 선택한 다음 필터링하는 것보다 회수율(recall)과 정확성을 개선해요. HNSW는 불변(offline) 및 변경 가능(consuming) 세그먼트에서 이 동작을 지원해요.

SELECT ProductId,
       Brand,
       cosineDistance(embedding, ARRAY[0.12, 0.34, 0.56, ...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY(embedding, ARRAY[0.12, 0.34, 0.56, ...], 50)
  AND category = 'electronics'
  AND inStock = true
ORDER BY dist ASC
LIMIT 10;

동작 방식:

  1. 메타데이터 필터(category = 'electronics')가 일치하는 행 ID의 비트맵을 구축.
  2. Pinot는 이를 사후 필터로 적용하는 대신 필수 범위(required scope)를 벡터 후보 생성에 전달.
  3. 희소 필수 범위의 경우 forward index를 사용할 수 있을 때 Pinot는 일치하는 forward-index 행에 대해서만 정확 스캔을 사용.
  4. 더 밀집된 범위이거나 forward index가 비활성화되면 Pinot는 비트맵을 필터링된 ANN에 전달.
  5. 일치하는 벡터만 top-K 후보 슬롯을 소비할 수 있음.

FULL-upsert 테이블의 경우 유효 문서 ID 비트맵도 필수 범위의 일부예요. 따라서 변경 가능한 HNSW는 consuming 세그먼트에서 후보를 선택하면서 오래된 행 버전을 제외해요. 변경 가능한 인덱스는 근실시간 서처를 사용하므로 새로 인덱싱된 consuming 행은 필터링된 검색에 계속 표시돼요.

변경 가능한 HNSW 새로고침 튜닝

consuming 세그먼트에 대한 필터링된 검색은 백그라운드 재개방(reopen)을 기다려 모든 필수 행을 볼 수 있게 해요. 같은 작성기 세대를 필요로 하는 동시 쿼리는 각 쿼리 스레드에서 HNSW 그래프를 재구축하는 대신 하나의 재개방을 공유해요. 이 HNSW 전용 properties가 그 핸드오프를 제어해요.

Property Default Behavior
refreshMinIntervalMs 1 근실시간 재개방 사이의 최소 시간. 간격을 없애려면 0으로 설정. 값이 클수록 작성기 플러시와 그래프 재구축이 줄지만, 새로 수집된 행을 기다리는 필터링된 쿼리에 같은 양의 지연을 추가할 수 있음.
refreshWaitTimeoutMs 5000 필터링된 쿼리가 필수 작성기 세대를 기다리는 최대 시간. 값은 0보다 커야 함.

재개방이 실패하거나 refreshWaitTimeoutMs를 초과하면 Pinot는 필터가 이름을 지정한 행을 생략할 수 있는 오래된 세대를 검색하는 대신 쿼리를 실패시켜요. 각 consuming 세그먼트와 벡터 컬럼은 하나의 데몬 재개방 스레드를 소유하므로 consuming 세그먼트나 벡터 컬럼이 많은 테이블 크기를 정할 때 그 스레드 수를 포함하세요.

필터 인지 벡터 검색을 언제 사용할까:

  • 벡터 조건자를 메타데이터 필터와 일반적으로 결합. 플래너가 세그먼트별로 정확 검색 또는 필터링된 ANN을 선택.
  • 희소 필터 성능이 중요할 때 벡터 컬럼의 forward index를 유지. 일치하는 행에 대한 정확 스캔을 가능하게 하기 때문.
  • 더 나은 순위 정확도가 필요할 때 ANN을 정확 재순위와 결합.

IVF_ON_DISK는 사전 필터 비트맵 계산, 필터 선택도를 보여주는 explain/debug 보고, 인메모리 IVF_FLAT 및 IVF_PQ와 일관된 동작을 포함한 전체 FILTER_THEN_ANN 지원을 가져요.

HNSW 런타임 튜닝

다음 쿼리 옵션은 인덱스를 재구축하지 않고 런타임에 HNSW 검색 동작을 제어해요. 변경 가능(consuming) 및 불변(offline) 세그먼트 모두에 적용돼요.

vectorEfSearch — 검색 빔 폭

그래프 탐색 중 HNSW가 방문하는 노드 수를 제어해요.

SET vectorEfSearch = 500;

SELECT ProductId,
       cosineDistance(embedding, ARRAY[...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY(embedding, ARRAY[...], 10)
ORDER BY dist ASC
LIMIT 10;

일반적인 값:

  • 100–150: 낮은 지연(실시간 애플리케이션)
  • 200–300: 균형(기본값)
  • 400–800: 높은 회수율(시맨틱 검색)

efSearch가 높을수록 쿼리 지연 비용으로 정확도가 개선돼요.

vectorUseRelativeDistance — 경쟁 가지치기

HNSW 그래프 탐색 중 경쟁 가지치기(competitive pruning)를 활성화하거나 비활성화해요. 일부 데이터 분포에서 비활성화하면 회수율이 개선될 수 있어요.

SET vectorEfSearch = 128;
SET vectorUseRelativeDistance = false;
SET vectorUseBoundedQueue = false;

SELECT cosineDistance(embedding, ARRAY[0.12, 0.34, 0.56]) AS dist, doc_id
FROM my_table
WHERE VECTOR_SIMILARITY(embedding, ARRAY[0.12, 0.34, 0.56], 10)
ORDER BY dist ASC LIMIT 10;

적응형 쿼리 플래너

Pinot는 필수 범위와 사용 가능한 인덱스에 따라 세그먼트별 실행 모드를 선택해요.

Required scope Available data Strategy
None Vector index 필수 범위 필터가 없는 순수 ANN
Sparse Forward index 일치하는 행 ID에 대한 정확 스캔
Dense Filter-aware vector index 필수 범위 비트맵을 사용한 필터링된 ANN
Sparse, but forward index disabled Filter-aware vector index 쿼리가 실행 가능하도록 필터링된 ANN
Any No usable vector index forward index를 사용한 정확 스캔

전략 구성은 필요 없어요. 필수 범위는 절대 ANN 후보 선택 후에만 적용되지 않으므로, 필터링되거나 오래된 upsert 행이 top-K 슬롯을 소비할 수 없어요.

쿼리 옵션

Option Default Description
vectorNprobe 4 프로브할 클러스터 (IVF_FLAT, IVF_PQ, IVF_ON_DISK)
vectorExactRerank true (IVF_PQ) ANN 후보의 정확 재순위에 대한 재정의
vectorMaxCandidates topK * 10 고려하는 ANN 후보 상한
vectorDistanceThreshold Not set 원시 Pinot 벡터 거리의 거리 임계값
vectorEfSearch From index config HNSW 전용: 검색 빔의 방문 예산
vectorUseRelativeDistance true HNSW 전용: 상대 거리 경쟁 가지치기 토글
vectorUseBoundedQueue true HNSW 전용: 경계 top-K 수집기 토글

벡터 검색 메트릭

VectorSearchMetrics는 다음 서버 측 카운터를 추적해요.

Metric Description
vectorAnnCandidatesRetrieved 인덱스에서 검색된 ANN 후보 수
vectorExactRerankCount 정확 거리 계산으로 재순위된 벡터
vectorFilteredOutCount 사전 필터 비트맵에 의해 제거된 벡터
vectorSearchLatencyMs 종단 간 검색 지연

인덱스 유형 비교

Index Memory Build Time Query Speed Recall Quantization Disk-Backed
HNSW Medium Moderate Fast Excellent — No
IVF_FLAT High Fast Medium Good FLAT/SQ8/SQ4 No
IVF_PQ Low Moderate Medium Fair Product Quantization No
IVF_ON_DISK Low Moderate Medium Good FLAT/SQ8/SQ4/PQ Yes

완전한 예시: 시맨틱 제품 검색

스키마

{
  "schemaName": "products",
  "dimensionFieldSpecs": [
    { "name": "ProductId", "dataType": "STRING" },
    { "name": "Category", "dataType": "STRING" },
    {
      "name": "embedding",
      "dataType": "FLOAT",
      "singleValueField": false
    }
  ]
}

테이블 구성

{
  "tableName": "products_OFFLINE",
  "fieldConfigList": [
    {
      "name": "Category",
      "indexes": { "inverted": {} }
    },
    {
      "name": "embedding",
      "encodingType": "RAW",
      "indexes": {
        "vector": {
          "vectorIndexType": "HNSW",
          "vectorDimension": 1536,
          "vectorDistanceFunction": "COSINE",
          "version": 1,
          "properties": {
            "maxCon": "32",
            "beamWidth": "200",
            "efConstruction": "400"
          }
        }
      }
    }
  ]
}

기본 Top-K 쿼리

SELECT ProductId,
       cosineDistance(embedding, ARRAY[-0.0013, -0.0110, ...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY(embedding, ARRAY[-0.0013, -0.0110, ...], 10)
ORDER BY dist ASC
LIMIT 10;

필터 인지 ANN 쿼리

SELECT ProductId,
       Category,
       cosineDistance(embedding, ARRAY[-0.0013, -0.0110, ...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY(embedding, ARRAY[-0.0013, -0.0110, ...], 50)
  AND Category = 'Electronics'
ORDER BY dist ASC
LIMIT 10;

반경 검색

SELECT ProductId,
       cosineDistance(embedding, ARRAY[-0.0013, -0.0110, ...]) AS dist
FROM products
WHERE VECTOR_SIMILARITY_RADIUS(embedding, ARRAY[-0.0013, -0.0110, ...], 0.25)
ORDER BY dist ASC;

정확 재순위가 있는 IVF

SET vectorNprobe = 16;
SET vectorMaxCandidates = 500;
SET vectorExactRerank = true;

SELECT l2Distance(embedding, ARRAY[1.0, 2.0, 3.0]) AS dist, doc_id
FROM my_table
WHERE VECTOR_SIMILARITY(embedding, ARRAY[1.0, 2.0, 3.0], 20)
ORDER BY dist ASC LIMIT 20;

고정 Top-K 없는 거리 임계값

SET vectorDistanceThreshold = 0.75;
SET vectorMaxCandidates = 500;

SELECT l2Distance(embedding, ARRAY[1.0, 2.0, 3.0]) AS dist, doc_id
FROM my_table
WHERE VECTOR_SIMILARITY(embedding, ARRAY[1.0, 2.0, 3.0], 200)
ORDER BY dist ASC LIMIT 200;

더 알아보기 (Learn more)