벡터 데이터베이스 (Vector Database, 벡터 데이터베이스)
2026.10.03 · 19분
1. 벡터 데이터베이스란?
벡터 데이터베이스(Vector Database)는 임베딩 벡터를 저장하고, 주어진 벡터와 가장 가까운 벡터들을 빠르게 찾아 주는 저장소입니다.
일반 데이터베이스가 "이 조건과 일치하는 행을 찾아라"에 최적화되어 있다면, 벡터 데이터베이스는 "이 벡터와 가장 비슷한 벡터 k개를 찾아라"에 최적화되어 있습니다.
일반 DB: SELECT * FROM docs WHERE category = '인사' (일치)
벡터 DB: 이 질문 벡터와 가장 가까운 문서 5개를 찾아라 (유사) 질문 벡터 q
│
▼
┌──────────────────────────────┐
│ 벡터 데이터베이스 │
│ ┌────────────────────────┐ │
│ │ 벡터 인덱스 (HNSW 등) │ │ ← 빠른 근사 검색
│ └────────────────────────┘ │
│ ┌────────────────────────┐ │
│ │ 원문, 메타데이터 │ │ ← 부서, 날짜, 권한, 출처
│ └────────────────────────┘ │
└──────────────┬───────────────┘
▼
가장 가까운 청크 k개 + 원문 + 메타데이터RAG에서 벡터 DB는 질문과 관련 있는 문서 조각을 꺼내 오는 창고 역할을 합니다.
벡터 DB가 하는 일은 이렇게 요약할 수 있습니다.
정확한 답 대신 거의 정확한 답을 아주 빠르게 돌려주는, 가까운 이웃 찾기 전용 저장소.
2. 왜 전수 비교로는 안 될까?
2-1. 전수 비교 (Brute-force, Flat 검색)
가장 단순한 방법은 질문 벡터와 모든 벡터를 하나씩 비교하는 것입니다.
import numpy as np
doc_vecs = np.random.randn(1_000_000, 1024).astype("float32")
doc_vecs /= np.linalg.norm(doc_vecs, axis=1, keepdims=True)
q = doc_vecs[0]
scores = doc_vecs @ q # 100만 번 내적
top5 = np.argpartition(-scores, 5)[:5]이 방식은 항상 정확한 답을 주지만 비용이 큽니다.
질문 1개당 계산량 ≈ 문서 수 × 차원 수
10만 청크 × 1024차원 ≈ 1억 번 곱셈 → 충분히 빠르다
1000만 청크 × 1024차원 ≈ 100억 번 곱셈 → 질문마다 이걸 하면 느리다
+ 동시 사용자 수백 명 → 감당하기 어렵다메모리도 문제입니다. 1000만 개 × 1024차원 × 4바이트면 벡터만 약 41GB입니다.
2-2. 기존 DB 인덱스가 도움이 안 되는 이유
B-Tree 인덱스는 "값의 크기 순서"로 정렬할 수 있어서 빠릅니다. 하지만 1024차원 벡터에는 "크기 순서"라는 게 없습니다. 차원이 높아지면 공간을 나누는 고전적인 방법(KD-Tree 등)도 효율이 급격히 떨어집니다. 이걸 흔히 차원의 저주(Curse of Dimensionality)라고 부릅니다.
2-3. 그래서: 조금 틀려도 되니 빨리
RAG에서 꼭 "수학적으로 가장 가까운 5개"가 필요할까요? 대부분은 가장 가까운 5개 중 4~5개만 맞아도 충분합니다. 어차피 임베딩 자체가 의미를 완벽하게 표현하지 못하기 때문입니다.
이 타협이 근사 최근접 이웃(ANN, Approximate Nearest Neighbor) 검색입니다.
| 구분 | 정확한 kNN (Flat) | 근사 ANN |
|---|---|---|
| 결과 | 항상 정확 | 대부분 정확 (일부 놓칠 수 있음) |
| 속도 | 데이터에 비례해 느려짐 |
3. ANN의 핵심 지표: Recall과 속도의 줄다리기
ANN의 품질은 보통 Recall@k로 잽니다.
Recall@10 = (ANN이 찾은 상위 10개 중 진짜 상위 10개에 속하는 개수) / 10
예) 진짜 상위 10개 중 9개를 찾았다 → Recall@10 = 0.9모든 ANN 알고리즘에는 더 많이 찾아볼수록 정확해지지만 느려지는 조절 손잡이가 있습니다.
정확도(Recall)
1.0 ┤ ●───●
│ ●
│ ●
│ ●
│ ●
│ ●
└──────────────────────────── 검색 시간
(탐색 범위를 늘릴수록 →)벡터 DB 튜닝은 대부분 원하는 Recall을 만족하는 가장 빠른 지점을 찾는 일입니다.
4. 주요 인덱스 알고리즘 직관
4-1. HNSW (Hierarchical Navigable Small World)
현재 가장 널리 쓰이는 그래프 기반 인덱스입니다. 2016년 Malkov와 Yashunin의 논문에서 제안되었습니다.
직관: 고속도로 → 국도 → 골목길
Layer 2 (적은 노드, 긴 연결) A ─────────────── F
│ │
Layer 1 (중간) A ──── C ──── E ── F
│ │ │ │
Layer 0 (모든 노드, 짧은 연결) A ─ B ─ C ─ D ─ E ─ F ─ G ─ H- 맨 위층에서 시작해 질문 벡터에 가장 가까워지는 방향으로 이웃을 따라 이동합니다.
- 더 이상 가까워지지 않으면 아래층으로 내려갑니다.
- 맨 아래층(모든 벡터가 있는 층)에서 주변을 꼼꼼히 탐색해 후보를 고릅니다.
서울에서 부산의 어느 골목을 찾을 때, 처음부터 골목길로 가지 않고 고속도로로 부산까지 간 뒤 국도, 골목 순으로 좁혀 가는 것과 같습니다.
| 파라미터 | 의미 | 키우면 |
|---|---|---|
M | 노드 하나가 연결하는 이웃 수 | 정확도↑, 메모리↑, 구축 시간↑ |
ef_construction | 인덱스를 만들 때 살펴보는 후보 수 | 인덱스 품질↑, 구축 시간↑ |
ef_search (ef) | 검색할 때 살펴보는 후보 수 |
5. 메타데이터 필터링
5-1. 왜 필요한가?
실제 RAG에서는 "가장 비슷한 문서"만으로는 부족합니다.
"인사팀 문서 중에서" → dept = 'hr'
"2025년 이후 개정된 규정만" → updated_at >= '2025-01-01'
"이 사용자가 볼 수 있는 것만" → acl contains user_group특히 권한 필터는 반드시 있어야 합니다. 권한 없는 문서가 검색되어 LLM 답변에 섞이면 그대로 정보 유출입니다.
5-2. 사전 필터 vs 사후 필터
| 방식 | 동작 | 문제 |
|---|---|---|
| 사후 필터 (Post-filter) | ANN으로 상위 k개를 찾은 뒤 조건에 안 맞는 것을 버린다 | 조건이 까다로우면 결과가 k개보다 적거나 0개가 된다 |
| 사전 필터 (Pre-filter) | 조건에 맞는 것만 대상으로 검색한다 | 단순 구현은 전수 비교가 되어 느려지고, 그래프 인덱스에서는 연결이 끊겨 Recall이 떨어질 수 있다 |
| 통합 필터 (Filtered ANN) | 인덱스 탐색 중에 조건을 함께 검사한다 | 구현이 복잡하다. 벡터 DB마다 품질 차이가 큰 부분 |
사후 필터의 함정
ANN 상위 10개 → [영업, 영업, 개발, 영업, 개발, 영업, 영업, 개발, 영업, 영업]
조건: dept = 'hr'
결과: 0개 ← 인사팀 문서가 분명히 있는데도 못 찾음벡터 DB를 고를 때는 "필터링 지원" 여부와 함께, 선택도가 낮은(조건에 맞는 문서가 아주 적은) 필터에서도 Recall이 유지되는지 확인해야 합니다.
5-3. Qdrant 예시
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue,
)
client = QdrantClient(":memory:") # 테스트용 인메모리 모드
client.create_collection(
"docs", vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
)
client.upsert("docs", points=[
PointStruct(id=1, vector=vec1, payload={"dept": "hr", "text": "휴가 신청은..."}),
PointStruct(id=2, vector=vec2, payload={"dept": "sales", "text": "분기 목표는..."}),
])
hits = client.query_points(
"docs",
query=query_vec,
query_filter=Filter(must=[FieldCondition(key="dept", match=MatchValue(value="hr"))]),
limit=5,
).points6. 주요 벡터 데이터베이스 비교
6-1. 분류
┌─────────────────────────────────────────────────────────┐
│ 라이브러리 FAISS, hnswlib, Annoy │ ← 인덱스만 제공
├─────────────────────────────────────────────────────────┤
│ 기존 DB 확장 pgvector(PostgreSQL), │ ← 익숙한 DB에 벡터 추가
│ Elasticsearch/OpenSearch kNN │
├─────────────────────────────────────────────────────────┤
│ 전용 벡터 DB Qdrant, Milvus, Chroma, Weaviate │ ← 벡터 검색 중심 설계
├─────────────────────────────────────────────────────────┤
│ 완전 관리형 서비스 Pinecone, 각 벡터 DB의 클라우드 버전 │ ← 운영을 맡김
└─────────────────────────────────────────────────────────┘6-2. 비교표
| 항목 | pgvector | Qdrant | Chroma | Pinecone | Milvus |
|---|---|---|---|---|---|
| 형태 | PostgreSQL 확장 | 전용 벡터 DB (오픈소스) | 전용 벡터 DB (오픈소스) | 완전 관리형 SaaS | 전용 벡터 DB (오픈소스) |
| 구현 언어 | C | Rust | Python/Rust | 비공개 | Go/C++ |
7. 선택 기준
7-1. 질문 순서
1. 벡터가 몇 개인가? (지금 + 1~2년 후)
└─ 수십만 개 이하 → Flat이나 pgvector로도 충분한 경우가 많다
2. 이미 쓰는 DB, 검색 엔진이 있는가?
└─ Postgres → pgvector / Elasticsearch·OpenSearch → 내장 kNN
3. 필터 조건이 복잡하거나, 권한 필터가 필수인가?
└─ 필터링 Recall을 직접 테스트한다
4. 직접 운영할 수 있는가?
└─ 아니오 → 관리형 서비스
5. 데이터가 외부 클라우드에 저장되어도 되는가?
└─ 아니오 → 자체 호스팅
6. 키워드 검색과 함께 써야 하는가?
└─ 하이브리드 검색 지원 여부 확인7-2. 체크리스트
| 항목 | 확인할 것 |
|---|---|
| 규모 | 벡터 수 × 차원 × 4바이트 + 인덱스 오버헤드가 메모리에 들어가는가 |
| 지연 시간 | p95, p99 응답 시간 요구사항 |
| 업데이트 | 문서가 얼마나 자주 추가, 수정, 삭제되는가 |
| 필터링 | 선택도가 낮은 필터에서 Recall이 유지되는가 |
| 멀티테넌시 | 고객사, 사용자별 데이터 격리 방식 |
| 하이브리드 | 희소 벡터, BM25를 함께 지원하는가 |
| 운영 | 백업, 복구, 모니터링, 버전 업그레이드 |
| 비용 | 관리형 요금 vs 서버 + 인건비 |
7-3. 흔한 실수
8. 언제 벡터 DB가 필요 없는가
| 상황 | 대안 |
|---|---|
| 문서가 수천~수만 청크 | numpy 전수 비교, FAISS Flat |
| 이미 Postgres 사용 중 | pgvector (새 시스템 추가 없이) |
| 정확한 키워드 검색이 주된 요구 | BM25 (Elasticsearch, OpenSearch) |
| 문서가 짧고 적어서 통째로 넣을 수 있음 | LLM 긴 컨텍스트에 직접 넣기 |
| 구조가 명확한 문서(목차, 조항) | 임베딩 없이 구조를 따라가는 Vectorless RAG |
9. 장점과 단점
| 장점 | 설명 |
|---|---|
| 대규모에서도 빠르다 | ANN 인덱스로 수백만~수억 개에서 밀리초 단위 검색 |
| 의미 검색의 기반 | 임베딩과 결합해 뜻으로 찾는다 |
| 메타데이터 필터 | 부서, 날짜, 권한 조건을 함께 건다 |
| 운영 기능 | 영속성, 복제, 백업, API (라이브러리 대비) |
| 단점 | 설명 |
|---|---|
| 결과가 근사값이다 | 가장 가까운 문서를 놓칠 수 있다 |
| 튜닝이 필요하다 | M, ef, nprobe 등 파라미터와 Recall/속도 균형 |
| 메모리를 많이 쓴다 | 특히 HNSW. 양자화로 보완 |
| 필터링이 까다롭다 | 필터와 ANN을 함께 잘하는 것은 어렵다 |
| 시스템이 하나 늘어난다 | 원본 DB와 동기화, 운영 부담 |
10. 핵심 정리
벡터 데이터베이스는 임베딩 벡터를 저장하고 질문 벡터와 가까운 벡터를 빠르게 찾아 주는 저장소다. 수백만 개 이상을 전수 비교하면 너무 느리기 때문에, 약간의 정확도를 포기하고 속도를 얻는 근사 최근접 이웃(ANN) 인덱스를 쓴다. HNSW는 계층 그래프를 따라 내려가며 찾고, IVF는 클러스터를 나눠 가까운 곳만 뒤지며, PQ는 벡터를 압축해 메모리를 줄인다. 실무에서는 메타데이터, 특히 권한 필터를 ANN과 함께 잘 처리하는지가 중요하다. 이미 Postgres를 쓰면 pgvector, 필터가 중요하면 Qdrant, 빠른 프로토타입은 Chroma, 운영을 맡기려면 Pinecone, 초대규모는 Milvus가 출발점이며, 데이터가 작다면 벡터 DB 없이 전수 비교로도 충분하다.