벡터DB 4대장(Pinecone·Weaviate·Qdrant·Chroma) 2026년 3월 실전 선택 가이드: “성능”은 벤치마크가 아니라 워크로드가 결정한다
들어가며
RAG/Agent 시스템이 “잘 답하는지”는 모델보다 retrieval layer(검색/필터/정렬)의 품질과 지연시간에 더 자주 발목 잡힙니다. 특히 2026년 현재는 임베딩 차원이 768~1536으로 커지고(예: 1536d), 메타데이터 필터(tenant, ACL, time range)와 hybrid search(BM25+vector), rerank까지 붙으면서 벡터DB 선택이 곧 아키텍처 선택이 됐습니다.
그런데 “어느 DB가 제일 빠르냐” 류의 질문은 대개 함정입니다. 벤더/커뮤니티 벤치마크는 각자의 스윗스팟(순수 ANN, hybrid, 필터링, 운영 편의성)을 과장하기 쉽고, 실제 병목은 필터 선택도, 인덱스 파라미터, 메모리 상주율, 동시성, 네트워크에서 갈립니다. Qdrant가 공개한 filtered ANN 벤치마크도 “필터링 여부가 성능을 크게 바꾼다”는 점을 전면에 둡니다. (qdrant.tech)
이번 글은 2026년 3월 시점의 공개 자료를 바탕으로, Pinecone/Weaviate/Qdrant/Chroma를 선택 가이드 + 성능 관점으로 정리합니다(“누가 1등”이 아니라 “내 조건에서 실패하지 않는 선택”).
🔧 핵심 개념
1) 벡터 검색 성능을 결정하는 4가지 축
Index 구조(대부분 HNSW 계열)
HNSW는 그래프 기반 ANN으로,M,efConstruction,efSearch같은 파라미터가 recall↔latency 트레이드오프를 만듭니다. 실무에선 “같은 DB”라도 파라미터/메모리/필터 비율에 따라 결과가 완전히 달라집니다.- Filtering(메타데이터/페이로드) 많은 RAG는
tenant_id,document_type,created_at같은 필터가 기본입니다. 여기서 흔한 실패는:- 필터가 후보를 너무 많이 남겨서 “거의 full scan처럼” 동작
- 반대로 필터가 너무 선택적이라 그래프 탐색이 비효율적 Qdrant는 이 지점을 핵심 차별점으로 잡고 filtered ANN 벤치마크/데이터셋까지 공개합니다. (qdrant.tech)
Hybrid search Weaviate는 hybrid(BM25+vector) 기능을 “개념”으로 강하게 전면화하고, BM25 파라미터까지 노출합니다. (weaviate.io)
즉, “유사도”만이 아니라 “키워드 정확도”를 같이 잡고 싶다면 hybrid가 사실상 필수 옵션이 됩니다.- 운영 모델(Managed vs Self-host) Pinecone은 managed로서 일관된 latency/운영 편의성을 강점으로, 요금도 On-Demand/Pods 등으로 세분화돼 있습니다. Pods 가격표는 임베딩 차원 768 기준 “대략 capacity”를 제시합니다. (pinecone.io)
반대로 Qdrant/Weaviate/Chroma는 self-host/오픈소스 기반의 통제력을 얻되, 튜닝·업그레이드·관측성 비용을 떠안습니다.
2) 2026년 3월 관찰: “Chroma는 dev-default, prod는 점점 분화”
Chroma는 여전히 “로컬에서 빨리 붙이는” 경험이 강점이지만, 2026년 3월 커뮤니티 트래킹 기반 기사에서는 프로덕션에서 Weaviate/Qdrant로 이동하는 흐름을 언급합니다(다운로드 지표/커뮤니티 반응 기반). (ai-buzz.com)
이건 Chroma가 나쁘다기보다, RAG가 “데모→운영”으로 넘어가며 요구사항(멀티테넌시, 필터, 관측성, 안정성)이 커졌다는 신호로 보는 게 현실적입니다.
3) (주의) 벤치마크는 “정답”이 아니라 “가설”
한 2026년 벤치마크 요약 글은 1M·1536d·topK=10·recall≥0.95 조건에서 Qdrant가 낮은 지연/높은 QPS를 보였다고 정리하면서도, “벤치마크 마케팅을 조심하라”고 못 박습니다. (core.cz)
여기서 진짜 takeaway는 순위가 아니라:
- 테스트 조건이 내 조건과 다르면 수치가 무의미
- 필터링/동시성/데이터 분포가 바뀌면 순위가 뒤집힘 입니다.
💻 실전 코드
아래는 “동일한 데이터/쿼리 형태로” Qdrant/Weaviate/Chroma를 빠르게 비교할 때 쓸 수 있는 최소 실행 예제입니다. (Pinecone은 managed 계정/키가 필요해서 코드 스켈레톤만 제공합니다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# python 3.11+
# pip install qdrant-client weaviate-client chromadb numpy
import os
import time
import numpy as np
N = 50_000
DIM = 768
TOPK = 10
def gen_vectors(n=N, dim=DIM, seed=42):
rng = np.random.default_rng(seed)
vectors = rng.normal(size=(n, dim)).astype(np.float32)
# 간단한 메타데이터(필터 테스트용)
payloads = [{"tenant": f"t{idx % 10}", "doc_type": "faq" if idx % 3 == 0 else "kb"} for idx in range(n)]
return vectors, payloads
vectors, payloads = gen_vectors()
query = vectors[123].tolist()
filter_tenant = "t3"
# -------------------------
# Qdrant (self-host: localhost:6333 가정)
# docker run -p 6333:6333 qdrant/qdrant
# -------------------------
def test_qdrant():
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
client = QdrantClient(url="http://localhost:6333")
col = "demo_vectors"
# recreate for repeatable benchmark
client.recreate_collection(
collection_name=col,
vectors_config=VectorParams(size=DIM, distance=Distance.COSINE),
)
# upsert batch
batch = []
for i in range(N):
batch.append(PointStruct(id=i, vector=vectors[i].tolist(), payload=payloads[i]))
t0 = time.time()
client.upsert(collection_name=col, points=batch)
upsert_s = time.time() - t0
# filtered search
qfilter = Filter(must=[
FieldCondition(key="tenant", match=MatchValue(value=filter_tenant)),
FieldCondition(key="doc_type", match=MatchValue(value="faq")),
])
# warmup
client.search(collection_name=col, query_vector=query, limit=TOPK, query_filter=qfilter)
t0 = time.time()
for _ in range(50):
client.search(collection_name=col, query_vector=query, limit=TOPK, query_filter=qfilter)
avg_ms = (time.time() - t0) / 50 * 1000
print(f"[Qdrant] upsert={upsert_s:.2f}s, filtered_search_avg={avg_ms:.2f}ms")
# -------------------------
# Weaviate (self-host: localhost:8080 가정)
# docker compose로 weaviate 띄우는 구성이 일반적
# -------------------------
def test_weaviate():
import weaviate
from weaviate.classes.config import Configure, Property, DataType
client = weaviate.connect_to_local(host="localhost", port=8080)
# schema
if client.collections.exists("DemoVectors"):
client.collections.delete("DemoVectors")
demo = client.collections.create(
name="DemoVectors",
vectorizer_config=Configure.Vectorizer.none(),
properties=[
Property(name="tenant", data_type=DataType.TEXT),
Property(name="doc_type", data_type=DataType.TEXT),
],
)
# insert
t0 = time.time()
with demo.batch.dynamic() as batch:
for i in range(N):
batch.add_object(
properties=payloads[i],
vector=vectors[i].tolist(),
)
upsert_s = time.time() - t0
# filtered near_vector (where filter)
# 주의: filter 표현은 client 버전에 따라 다를 수 있음
# 여기서는 개념적 예제(실행 시에는 설치 버전에 맞춰 수정 필요)
filt = (
weaviate.classes.query.Filter
.by_property("tenant").equal(filter_tenant)
.and_filter(weaviate.classes.query.Filter.by_property("doc_type").equal("faq"))
)
# warmup
demo.query.near_vector(near_vector=query, limit=TOPK, filters=filt)
t0 = time.time()
for _ in range(50):
demo.query.near_vector(near_vector=query, limit=TOPK, filters=filt)
avg_ms = (time.time() - t0) / 50 * 1000
print(f"[Weaviate] upsert={upsert_s:.2f}s, filtered_search_avg={avg_ms:.2f}ms")
client.close()
# -------------------------
# Chroma (로컬 임베디드)
# -------------------------
def test_chroma():
import chromadb
client = chromadb.PersistentClient(path="./chroma_data")
try:
client.delete_collection("demo_vectors")
except Exception:
pass
col = client.create_collection(name="demo_vectors", metadata={"hnsw:space": "cosine"})
ids = [str(i) for i in range(N)]
metadatas = payloads
documents = [""] * N # 문서 본문은 생략 가능
t0 = time.time()
# Chroma는 add에서 embedding을 직접 넣을 수 있음
col.add(ids=ids, embeddings=vectors.tolist(), metadatas=metadatas, documents=documents)
upsert_s = time.time() - t0
# filtered query (where)
where = {"$and": [{"tenant": filter_tenant}, {"doc_type": "faq"}]}
# warmup
col.query(query_embeddings=[query], n_results=TOPK, where=where)
t0 = time.time()
for _ in range(50):
col.query(query_embeddings=[query], n_results=TOPK, where=where)
avg_ms = (time.time() - t0) / 50 * 1000
print(f"[Chroma] upsert={upsert_s:.2f}s, filtered_search_avg={avg_ms:.2f}ms")
if __name__ == "__main__":
# 환경에 맞는 것만 실행하세요.
test_qdrant()
# test_weaviate()
test_chroma()
# Pinecone은 아래처럼 "동일한 쿼리/필터 패턴"으로 측정하되,
# managed 특성상 네트워크/region 영향이 크므로 반드시 같은 VPC/region에서 재현하세요.
#
# from pinecone import Pinecone
# pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
# index = pc.Index("YOUR_INDEX")
# index.query(vector=query, top_k=TOPK, filter={"tenant": {"$eq": filter_tenant}, "doc_type": {"$eq":"faq"}})
⚡ 실전 팁
1) 선택 가이드(요약)
- Pinecone: “운영 리스크를 돈으로 해결”하는 선택. SLA/백업/권한/RBAC 같은 비기능 요구사항이 크면 강합니다. 가격은 Pods/On-Demand로 제시되며, Pods는 용량/시간 단가가 명확합니다. (pinecone.io)
- Qdrant: “필터링+성능+비용” 균형을 노리기 좋습니다. 특히 payload filter가 많은 RAG에서 강점을 강조하며, filtered ANN 벤치마크/데이터셋을 공개해 ‘필터가 성능을 바꾼다’를 전제로 접근합니다. (qdrant.tech)
- Weaviate: hybrid search와 검색 품질(키워드+벡터) 축에서 존재감이 큽니다. hybrid search가 BM25 파라미터까지 포함해 설계돼 있다는 점은 “의미 검색만으로 부족한 도메인(이슈 트래커/문서/FAQ)”에서 특히 유리합니다. (weaviate.io)
- Chroma: 로컬/내장형으로 개발 속도가 빠른 편이지만, 2026년 3월 관찰에서는 프로덕션 요구사항이 커질수록 Weaviate/Qdrant로 옮기는 흐름도 언급됩니다. (ai-buzz.com)
2) “성능 비교”를 제대로 하려면 체크리스트부터 고정
- 고정해야 할 것: dim(768/1536), topK, recall 목표(예: ≥0.95), 필터 선택도(예: tenant 10개 중 1개), 동시성(1/16/64), warmup 여부, 데이터 삽입 방식(stream vs bulk).
- 반드시 재현할 것: 필터 없는 쿼리 vs 필터 있는 쿼리. Qdrant 벤치마크가 이걸 별도로 다루는 이유가 있습니다. (qdrant.tech)
3) 운영 관점의 “진짜 비용”: 튜닝/관측성/마이그레이션
- self-host는 인프라 비용만 보면 싸 보이지만, 인덱스 리빌드 시간, 스키마 변경, 버전 업, 모니터링/알람이 누적되면 인건비가 더 큽니다.
- managed는 반대로 “비싸지만 예측 가능”합니다. Pinecone은 Standard 플랜에서 최소 과금/사용량 기반을 명시하고 있습니다. (pinecone.io)
4) 결론적으로, 벤치마크는 “1차 필터”로만 써라
어떤 비교 글은 특정 조건(1M·1536d 등)에서 Qdrant가 매우 좋은 수치를 보였다고 정리하지만, 동시에 벤치마크 마케팅을 경계하라고 강조합니다. (core.cz)
실무에서는:
- “내 데이터+내 필터+내 동시성”으로 P95/P99를 찍어보고
- 결과가 비슷하면 “운영 난이도/팀 역량/락인”으로 결정 하는 게 실패 확률이 낮습니다.
🚀 마무리
2026년 3월 기준으로 Pinecone/Weaviate/Qdrant/Chroma는 “기능이 겹친다”기보다 서로 다른 실패를 막아주는 제품으로 분화했습니다.
- 운영 안정성과 관리형 경험이 최우선이면 Pinecone(비용은 문서 기준으로 구조가 명확). (pinecone.io)
- 필터링이 많은 RAG에서 성능/비용 밸런스를 잡고 싶다면 Qdrant(필터 중심 벤치마크/접근이 강점). (qdrant.tech)
- 검색 품질(키워드+의미)의 일관성이 필요하면 Weaviate의 hybrid 축을 진지하게 보자. (weaviate.io)
- 개발 속도/로컬 실험은 Chroma가 여전히 강하지만, 프로덕션 요구가 커지면 다른 선택지로의 이동 가능성을 처음부터 설계에 넣어두는 게 좋다. (ai-buzz.com)
다음 학습 추천: 1) “내 워크로드로 벤치마크 만드는 법”: dim/필터선택도/동시성/recall 목표를 고정하고 P95/P99를 수집하는 스크립트를 CI에 넣기
2) “Hybrid + rerank 설계”: BM25+vector의 score calibration, RRF, rerank top-N 비용 모델링(Weaviate의 검색 품질 벤치마크 글이 좋은 출발점) (weaviate.io)