"빠르고 정확하게"는 공짜가 아니다
벡터 데이터베이스는 근사 최근접 이웃(ANN, Approximate Nearest Neighbor)을 반환한다. 정확도(Recall)와 처리량(QPS)·지연(Latency)은 반비례한다. 인덱스 탐색 범위를 넓히면 더 많은 진짜 이웃을 찾지만 그만큼 더 많은 후보를 계산한다.
이 챕터는 이 트레이드오프를 측정하고, 인덱스별 핵심 파라미터를 조정하며, 프로덕션에서 실제로 벤치마킹하는 방법을 다룬다.
Recall@K: 정확도 측정의 기본 단위
Recall@K는 가장 널리 쓰이는 ANN 정확도 지표다.
Recall@K = |반환된 K개 ∩ 진짜 Top-K| / K예: K=10으로 검색했을 때 반환된 10개 중 실제 Top-10에 속하는 것이 9개면 Recall@10 = 0.90.
진짜 Top-K는 전체 벡터에 대해 brute-force(FLAT 인덱스)로 계산한 결과다. 따라서 Recall 측정에는 별도의 정답 셋(ground-truth)이 필요하다.
HNSW 파라미터 튜닝
HNSW는 빌드 시 파라미터와 쿼리 시 파라미터가 분리된다.
빌드 파라미터 (인덱스 생성 시 결정)
| 파라미터 | 역할 | 기본값 | 조정 지침 |
|---|---|---|---|
M | 노드당 최대 엣지 수 | 16 | 높을수록 recall↑, 메모리↑. 범용: 16. 고recall: 32~64 |
ef_construction / efConstruction | 빌드 시 후보 풀 크기 | 100~200 | 최소 M × 2. 고품질 빌드: 400~800 |
M과 ef_construction은 인덱스 재빌드 없이 변경 불가다. 빌드 시 충분히 높게 설정하는 것이 중요하다.
쿼리 파라미터 (쿼리마다 조정 가능)
| 파라미터 이름 | 엔진별 표기 | 역할 |
|---|---|---|
ef (search) | pgvector: hnsw.ef_search, Qdrant: hnsw_ef, Milvus: ef | 쿼리 시 탐색 후보 풀 크기 |
ef_search 효과:
- ef = 32 → 빠름, recall 약 0.85~0.90
- ef = 64 → 균형, recall 약 0.92~0.95
- ef = 128 → 느림, recall 약 0.97~0.99
- ef = 512 → 매우 느림, recall 0.995+실용 규칙: ef_search = 2 × K부터 시작해 recall을 측정하며 목표치까지 올린다.
ef_search와 지연·QPS 관계
IVF 계열 파라미터 튜닝
IVF(Inverted File Index)는 k-means로 벡터 공간을 nlist개 클러스터로 분할하고, 쿼리 시 nprobe개 클러스터만 탐색한다.
빌드 파라미터
| 파라미터 | 역할 | 권장 설정 |
|---|---|---|
nlist | 클러스터 수 | sqrt(N) 이상. 100만 벡터 → 1024~4096 |
쿼리 파라미터
| 파라미터 | 역할 | 범위 |
|---|---|---|
nprobe | 탐색할 클러스터 수 | 1 ~ nlist. nprobe/nlist = 탐색 비율 |
nprobe 튜닝:
- nprobe = 1 → 최고속, recall 낮음 (~0.6~0.7)
- nprobe = 16 → 균형 (~0.90)
- nprobe = 64 → 고정확 (~0.97)
- nprobe = nlist → FLAT과 동일 (완전 탐색)IVF는 필터링과 궁합이 나쁘다: 필터 조건이 강할수록 각 클러스터 내 후보가 줄어들어 nprobe를 크게 올려야 recall을 유지할 수 있다. 이 경우 HNSW가 더 나은 선택이다.
인덱스별 튜닝 요약표
| 인덱스 | 빌드 파라미터 | 쿼리 파라미터 | 메모리 | 필터 대응 |
|---|---|---|---|---|
| FLAT | 없음 | 없음 | 벡터 크기 그대로 | 완벽 |
| IVF_FLAT | nlist | nprobe | 벡터 + 클러스터 중심 | 보통 |
| IVF_SQ8 | nlist | nprobe | 1/4 | 보통 |
| IVF_PQ | nlist, m, nbits | nprobe | 1/m~ | 보통 |
| HNSW | M, ef_construction | ef | 벡터 + 그래프 | 좋음 |
| DiskANN | search_list | search_list | 10% (나머지 SSD) | 보통 |
벤치마킹 도구
ANN-Benchmarks
표준 오픈소스 벤치마크. 고정 데이터셋(SIFT-1M, GIST-1M, GloVe 등)으로 Recall vs QPS Pareto 곡선을 생성한다.
# ANN-Benchmarks 실행 예시
python run.py --dataset sift-128-euclidean \
--algorithm hnswlib \
--runs 3
python plot.py --dataset sift-128-euclideanPareto 곡선: 같은 QPS에서 recall이 가장 높거나, 같은 recall에서 QPS가 가장 높은 설정만 표시. 우측 하단에 가까울수록 우수한 알고리즘·설정이다.
한계: 단일 스레드·소규모 데이터셋 중심이라 멀티스레드 프로덕션 환경을 그대로 반영하지 않는다.
VDBBench (구 VectorDBBench)
Zilliz가 공개한 실사용 환경 벤치마크. 지속 부하(sustained concurrency), P99 지연, Recall을 동시에 측정한다.
VDBBench 측정 흐름:
1. Serial test (1,000 쿼리, 단일 스레드)
→ recall, P99 latency 이론적 상한 파악
2. Concurrency test (점진적 동시 접속 증가)
→ 최대 지속 QPS (max_qps) 탐색
3. 각 설정에서 recall 함께 기록
→ recall과 QPS를 함께 비교핵심: 성능 숫자는 recall과 항상 같이 봐야 한다. recall 0.8에서 100,000 QPS는 recall 0.99에서 10,000 QPS보다 "빠른 것"이 아니라 "부정확한 것"일 수 있다.
실전 튜닝 워크플로
필터링과 Recall
필터 조건이 포함된 벡터 검색(Filtered ANN)은 별도로 처리해야 한다.
문제: 필터 비율에 따른 Recall 저하
전체 1억 벡터, 필터로 100만 벡터만 대상 (1% 선택성)
IVF_FLAT, nprobe=64:
→ 탐색 클러스터의 99%가 필터에서 탈락
→ 실제 후보가 거의 없어 Recall 급락
HNSW:
→ 필터 인식 그래프가 없으면 동일 문제 발생
→ 필터 인식 HNSW(Qdrant 방식) 또는 pre-filtering 필요대응 전략
| 선택성 | 권장 전략 |
|---|---|
| 높음 (>50%) | 필터 후 ANN (post-filtering) |
| 중간 (5~50%) | 엔진별 필터 인식 ANN 또는 과샘플링(nprobe↑, ef↑) |
| 낮음 (<5%) | Pre-filtering: 인덱스 자체를 서브셋으로 분리 |
Recall 측정 시: 필터 조건을 포함한 정답 셋을 별도로 만들어야 한다. 필터 없는 정답 셋 기준으로 Recall을 측정하면 필터 대응 품질을 알 수 없다.
양자화와 Recall
양자화는 메모리를 줄이는 대신 Recall을 일부 희생한다. 재순위(Rescore/Oversampling)로 대부분 회복 가능하다.
| 방법 | 압축 | Recall 손실 (재순위 없이) | 재순위 후 Recall |
|---|---|---|---|
| SQ8 (int8) | 4× | 1~2%p | ≈ 원본 |
| BQ (1bit) | 32× | 10~30%p | 원본의 95~99% |
| PQ (x16) | 16× | 5~15%p | 원본의 92~98% |
재순위는 oversampling × K개 후보를 양자화 인덱스로 뽑은 뒤 원본 float32 벡터로 재계산한다. oversampling = 2~3이 실용적 기본값이다.
흔한 실수와 대응
| 실수 | 결과 | 대응 |
|---|---|---|
| Recall을 측정하지 않고 QPS만 비교 | 부정확한 검색 서비스 | 모든 성능 측정에 Recall 함께 기록 |
| 개발 데이터로 튜닝 후 프로덕션 적용 | 분포 차이로 Recall 하락 | 프로덕션 샘플로 별도 벤치마킹 |
| ef_search를 매우 높게 고정 | 불필요한 지연 | 목표 Recall 달성 최소값으로 설정 |
| 필터 조건 변경 후 Recall 재확인 안 함 | 조용한 품질 저하 | 필터 변경 시 Recall 회귀 테스트 |
| 인덱스 타입만 바꾸고 파라미터 재조정 안 함 | 서브옵티멀 결과 | 인덱스 변경 시 항상 전체 파라미터 재스윕 |
References
- ANN-Benchmarks: A Benchmarking Tool for Approximate Nearest Neighbor Algorithms (GitHub)
- VDBBench 1.0: Real-World Benchmarking for Vector Databases — Milvus Blog
- VectorDBBench GitHub (Zilliz)
- A Practical Guide to Selecting HNSW Hyperparameters — OpenSearch Blog
- HNSW vs LSH: Elasticsearch hits 0.99 Recall@10 at 15,000 QPS — Elastic Blog
- Vector Index Tuning: HNSW, IVF & Product Quantization (AppScale Blog)
- Filtered Approximate Nearest Neighbor Search: System Design and Performance Analysis (arXiv 2602.11443)
- Semantic Recall for Vector Search (arXiv 2604.20417)
- pgvector Performance: Benchmark Results and 5 Ways to Boost Performance — Instaclustr
- Qdrant Binary Quantization — Vector Search 40x Faster