index-explained
Milvus 인덱스 (Index Explained)
인덱스는 데이터 위에 추가로 쌓는 구조로, 검색을 빠르게 만들어주는 대가로 전처리 시간·공간·RAM 비용이 늘어나요. 또 인덱스를 쓰면 대개 재현율(recall)이 다소 낮아질 수 있어요. 그래서 인덱스의 비용은 최소화하면서 이점은 최대화하는 선택이 필요합니다. 이 페이지는 소스 문서 Milvus — Index Explained 를 바탕으로 인덱스의 구조와 선택 기준을 정리해 드려요.
Milvus에서 인덱스는 필드(field)에 특화되며, 적용 가능한 인덱스 타입은 대상 필드의 데이터 타입에 따라 달라져요. 벡터 DB로서 Milvus는 벡터 검색과 스칼라 필터링 성능을 함께 끌어올리기 위해 다양한 인덱스 타입을 제공합니다.
필드 타입별 적용 가능한 인덱스
| 필드 데이터 타입 | 적용 가능한 인덱스 타입 |
|---|---|
| FLOAT_VECTOR | FLAT, IVF_FLAT, IVF_SQ8, IVF_PQ, IVF_RABITQ, HNSW, HNSW_SQ, HNSW_PQ, HNSW_PRQ, DISKANN, SCANN, AISAQ, FAISS, GPU_CAGRA, GPU_IVF_FLAT, GPU_IVF_PQ, GPU_BRUTE_FORCE |
| FLOAT16_VECTOR · BFLOAT16_VECTOR · INT8_VECTOR | FLAT, IVF_FLAT, IVF_SQ8, IVF_PQ, IVF_RABITQ, HNSW, HNSW_SQ, HNSW_PQ, HNSW_PRQ, DISKANN, SCANN, AISAQ, GPU_CAGRA, GPU_IVF_FLAT, GPU_IVF_PQ, GPU_BRUTE_FORCE |
| BINARY_VECTOR | BIN_FLAT, BIN_IVF_FLAT, MINHASH_LSH, FAISS |
| SPARSE_FLOAT_VECTOR | SPARSE_INVERTED_INDEX |
| VARCHAR | INVERTED(권장), BITMAP, Trie |
| BOOL | BITMAP(권장), INVERTED |
| INT8 · INT16 · INT32 · INT64 | INVERTED, STL_SORT |
| FLOAT · DOUBLE | INVERTED |
| ARRAY(요소가 BOOL·INT·VARCHAR) | BITMAP(권장) |
| ARRAY(요소가 BOOL·INT·FLOAT·DOUBLE·VARCHAR) | INVERTED |
| JSON | INVERTED |
스칼라 필드는 권장 인덱스 타입을 쓰면 되고, 이 문서는 벡터 인덱스 선택에 집중해요.
벡터 인덱스의 구조
벡터 인덱스 타입은 크게 데이터 구조(data structure), 양자화(quantization), 리파이너(refiner) 세 요소로 이뤄집니다. 양자화와 리파이너는 선택 사항이지만, 이점이 비용보다 크기 때문에 널리 쓰여요.
인덱스 생성 시 Milvus는 선택된 데이터 구조와 양자화 방법을 조합해 최적의 확장률(expansion rate) 을 정합니다. 쿼리 시점에는 topK × expansion rate 개의 후보 벡터를 가져온 뒤, 리파이너가 더 높은 정밀도로 거리를 다시 계산해 최종적으로 가장 정확한 topK를 반환해요. 정밀 계산을 좁힌 후보 부분집합에만 적용하므로 속도와 정확도의 균형을 맞출 수 있어요.
데이터 구조
- IVF(역파일) — 중심점(centroid) 기반 파티셔닝으로 벡터를 버킷(cluster)에 묶습니다. 중심이 질문 벡터와 가깝다면 그 버킷의 벡터들도 가까울 가능성이 높다는 전제를 활용해, 가까운 중심의 버킷만 스캔합니다. 대규모 데이터셋에서 빠른 처리량에 적합해요.
- 그래프 기반 — HNSW 같은 구조가 계층 그래프를 만들어 각 벡터를 가장 가까운 이웃들과 연결하고, 거친 상위 레이어에서 시작해 하위 레이어로 내려가며 탐색합니다. 높은 차원이나 저지연 쿼리 시나리오에 강해요.
양자화(Quantization)
- 스칼라 양자화(SQ8) — 각 벡터 차원을 1바이트(8bit)로 압축해 32비트 float 대비 메모리를 약 75% 줄이면서도 합리적 정확도를 유지합니다.
- 제품 양자화(PQ) — 벡터를 서브벡터로 나눠 코드북 기반 클러스터링으로 인코딩합니다. 4~32배의 높은 압축률을 얻는 대신 재현율이 조금 낮아질 수 있어, 메모리가 제약된 환경에 적합합니다.
리파이너(Refiner)
양자화는 본질적으로 손실이 있어요. 재현율을 유지하기 위해 양자화는 필요한 것보다 많은 top-K 후보를 만들고, 리파이너가 더 높은 정밀도로 그 후보들에서 실제 top-K를 골라 재현율을 높입니다. 예를 들어 FP32 리파이너는 양자화된 값 대신 FP32 정밀도로 후보들의 거리를 다시 계산해요.
이런 계층 구조 — 데이터 구조로 대략 여과, 양자화로 효율적 계산, 리파이너로 정밀도 조정 — 를 통해 Milvus는 정확도-성능 트레이드오프를 적응적으로 최적화합니다.
성능 트레이드오프와 선택 기준
성능을 평가할 때는 인덱스 구축 시간(Build time), 초당 쿼리 수(QPS), 재현율(Recall) 을 함께 고려해야 해요. 일반적인 규칙은 다음과 같습니다.
- 그래프 기반 인덱스는 대개 IVF 계열보다 QPS가 높아요.
- IVF 계열은 큰
topK(예: 2,000 이상) 시나리오에 잘 맞습니다. - 같은 압축률 대비 PQ가 SQ보다 재현율이 좋지만, SQ가 더 빠릅니다.
- DiskANN처럼 인덱스 일부를 하드디스크에 두면 대규모 데이터셋을 다룰 수 있지만 IOPS 병목이 생길 수 있어요.
용량(Capacity)
- 원본 데이터의 1/4이 메모리에 들어가면 안정적 지연 시간을 위해 DiskANN을 고려해요.
- 원본 데이터 전체가 메모리에 들어가면 메모리 기반 인덱스와 mmap을 고려합니다.
- 최대 용량을 위해 정확도를 일부 내어줘도 된다면 양자화 적용 인덱스와 mmap을 쓰면 됩니다. 단, 대부분의 데이터가 디스크에 있으면 DiskANN이 더 나은 지연 시간을 제공해요.
재현율(Recall)
- 필터 비율이 85% 미만이면 그래프 기반이 IVF 계열보다 우수합니다.
- 필터 비율이 85~95% 사이면 IVF 계열을 사용합니다.
- 필터 비율이 98%를 넘으면 가장 정확한 결과를 위해 Brute-Force(FLAT)를 씁니다.
성능(Performance)
- 작은
topK(예: 2,000)면서 높은 재현율이 필요하면 그래프 기반이 IVF 계열보다 낫습니다. topK가 전체 벡터 수 대비 크면 그래프 기반보다 IVF 계열이 더 나은 선택입니다.- 중간 크기
topK에 높은 필터 비율이면 IVF 계열이 유리해요.
선택 의사결정 매트릭스
| 시나리오 | 권장 인덱스 | 비고 |
|---|---|---|
| 원본 데이터가 메모리에 적합 | HNSW, IVF + Refinement | 낮은 k·높은 재현율이면 HNSW |
| 원본 데이터가 디스크(SSD) | DiskANN | 지연 민감 쿼리에 최적 |
| 원본 데이터 디스크에, RAM 제한 | IVFPQ/SQ + mmap | 메모리와 디스크 접근 균형 |
| 필터 비율 높음(>95%) | Brute-Force(FLAT) | 후보가 작아 인덱스 오버헤드 회피 |
큰 k(데이터셋의 1% 이상) |
IVF | 클러스터 프루닝이 계산량을 줄임 |
| 극도로 높은 재현율(>99%) | Brute-Force(FLAT) + GPU | — |
메모리 사용량 추정
인덱스의 메모리 소비는 데이터 구조, 양자화 압축률, 리파이너에 영향을 받아요. 그래프 기반 인덱스(HNSW)는 벡터당 공간 오버헤드가 커 메모리 사용량이 높고, IVF 계열은 벡터당 오버헤드가 작아 더 메모리 효율적입니다. DiskANN은 그래프나 리파이너 일부를 디스크에 두어 메모리 부담을 줄입니다.
예시 — 128차원 벡터 100만 개를 IVF로 인덱싱할 때:
- 중심(centroid): 2,000 클러스터 × 128차원 × 4바이트 = 약 1.0MB
- 클러스터 할당 ID: 1,000,000 벡터 × 2바이트 = 약 2.0MB
- 양자화(PQ, 8바이트/벡터): 1,000,000 × 8바이트 = 8.0MB → 합계 약 11MB
- (SQ8이면) 1,000,000 × 128차원 × 1바이트 = 128MB
- 리파이너 오버헤드(top10, 확장률 5): 50 후보 × 128차원 × 4바이트 = 25.6KB
HNSW로 같은 데이터를 인덱싱하면:
- 그래프 구조(차수 32): 1,000,000 × 32 링크 × 4바이트 = 128MB
- 원본 FP32 벡터: 1,000,000 × 128차원 × 4바이트 = 512MB → 합계 약 640MB
- PQ(8바이트/벡터)로 압축하면 벡터는 8MB가 되어 HNSWPQ 총 약 136MB (원본 대비 64배 압축)
데이터셋이 RAM을 넘는 상황은 DiskANN(Vamana 그래프를 디스크에 저장, PQ로 벡터 축소)과 mmap(디스크의 큰 파일에 직접 메모리 접근, 컬렉션 용량 확장)으로 대응합니다.
실제 적용 (데이터스케쳐스)
- 대규모 문서는 HNSW 기본 — 높은 재현율과 속도가 필요한 일반 RAG에 잘 맞습니다.
- 메모리 제약 시 IVF_PQ/SQ — 양자화로 메모리를 줄여 비용을 통제합니다.
- 거리 척도와 인덱스 일치 — 인덱스 타입과 함께
COSINE·L2·IP같은 거리 척도를 일관되게 설정합니다.
더 알아보기
- 공식 문서 (1차): Index Explained, Index Vector Fields, Similarity Metrics, Sparse Vector
- 큐레이션 (2차): Zilliz 블로그, How to Choose an Index in Milvus