포스트

2월 2026 기준 벡터DB 선택 가이드: Pinecone vs Weaviate vs Qdrant vs Chroma 성능·비용·운영성 한 방에 정리

2월 2026 기준 벡터DB 선택 가이드: Pinecone vs Weaviate vs Qdrant vs Chroma 성능·비용·운영성 한 방에 정리

들어가며

RAG, semantic search, 추천 시스템이 “서비스”가 되면서 벡터 검색은 더 이상 실험용 기능이 아닙니다. 문제는 임베딩을 저장하는 것보다 “얼마나 일정한 latency로, 얼마나 많은 QPS를, 어떤 필터/하이브리드 조건에서도” 보장할 수 있느냐입니다.
2026년 2월 시점에서 Pinecone/Weaviate/Qdrant/Chroma는 모두 벡터 검색을 제공하지만, 운영 모델(Managed vs Self-host), 인덱스/압축 전략, 필터 성능, 비용 예측 가능성에서 성격이 꽤 다릅니다. 특히 Pinecone은 고QPS용 “Dedicated Read Nodes(DRN)”로 읽기 경로를 분리하며 성능/비용 예측을 강화했고, Weaviate·Qdrant는 압축(quantization) + 필터 최적화를 문서화된 수준으로 적극 밀고 있습니다. (pinecone.io)


🔧 핵심 개념

1) 벡터DB 선택을 좌우하는 4가지 축

  1. 검색 알고리즘/인덱스: 대부분 HNSW 계열(근사 최근접)로 고속 검색을 합니다. 다만 “얼마나 메모리 친화적인가”, “재빌드/튜닝 난이도는?”가 제품마다 다릅니다.
  2. 필터 성능(= payload/metadata + query planner): RAG에서는 tenant_id, doc_type, created_at 같은 필터가 사실상 필수라서 “벡터만 빠른 DB”는 실무에서 금방 한계가 옵니다. Qdrant는 payload index를 별도로 두고, filter cardinality를 추정해 전략을 고르는 점을 명확히 설명합니다. (qdrant.tech)
  3. 하이브리드 검색: 키워드(BM25/BM25F) + 벡터를 섞어 “정확히 포함해야 하는 단어”와 “의미 유사성”을 동시에 잡습니다. Weaviate는 hybrid search에서 두 검색을 병렬 실행 후 score fusion(예: relativeScoreFusion)으로 랭킹을 합칩니다. (docs.weaviate.io)
  4. 압축(Quantization)과 rescoring: 메모리/비용을 줄이기 위해 벡터를 압축해 1차 후보를 빠르게 찾고, 상위 후보는 원본 벡터로 다시 점수 계산(rescoring)해 품질을 복구합니다. Weaviate는 PQ/RQ 등과 rescoring 개념을 문서로 강하게 밀고, Qdrant도 scalar/product/binary quantization의 trade-off 테이블을 제공합니다. (docs.weaviate.io)

2) 2026-02 관점의 제품별 “성격” 요약

  • Pinecone: 완전 Managed 중심. 2026년 초 기준 DRN(전용 읽기 노드)로 고QPS·예측 가능한 latency/cost를 노리는 구성이 눈에 띕니다. 공개된 사례로 수억~10억 벡터에서 수천 QPS, p99 수십~백ms대 수치를 언급합니다(워크로드/조건에 따라 다름). (pinecone.io)
  • Weaviate: hybrid search(BM25F+vector) + 다양한 vector compression(PQ/SQ/RQ/BQ) + dynamic index 같은 “검색 품질/자원 효율” 옵션이 강점입니다. 특히 RQ 8-bit는 4x 압축과 98~99% recall(내부 테스트)을 언급합니다. (docs.weaviate.io)
  • Qdrant: Rust 기반, payload filtering과 인덱싱 전략을 비교적 명확히 노출합니다. payload index 타입(keyword, integer, text 등)과 on-disk payload index까지 제공해 “필터 중심 설계”에 유리합니다. 또한 quantization을 scalar/product/binary로 구체적으로 튜닝 가능합니다. (qdrant.tech)
  • Chroma: 개발/프로토타이핑 친화적이고 API가 단순합니다. 다만 PersistentClient 사용 시 이슈(예: 2025-11에 보고된 메모리 누수 이슈) 같은 “프로덕션 운영 리스크”가 눈에 띄어, 규모가 커질수록 검증이 필요합니다. (docs.trychroma.com)

💻 실전 코드

아래 예제는 “같은 데이터”를 Qdrant/Weaviate/Chroma에 넣고, 벡터 검색 + metadata 필터를 걸어보는 최소 실행 코드입니다. (Pinecone은 계정/키/인덱스 설정이 필수라 여기선 로컬 재현이 쉬운 3개로 비교합니다.)

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
# Python 3.11+
# pip install qdrant-client weaviate-client chromadb

from qdrant_client import QdrantClient
from qdrant_client.http import models as qm

import weaviate
import weaviate.classes as wc

import chromadb


DIM = 4
docs = [
    {"id": "a", "text": "kubernetes autoscaling guide", "tenant": "t1", "vec": [0.9, 0.1, 0.0, 0.0]},
    {"id": "b", "text": "postgres index tuning",        "tenant": "t1", "vec": [0.1, 0.9, 0.0, 0.0]},
    {"id": "c", "text": "vector search filtering",      "tenant": "t2", "vec": [0.0, 0.1, 0.9, 0.0]},
]
query_vec = [0.95, 0.05, 0.0, 0.0]

# -------------------------
# 1) Qdrant: payload index + filter
# 핵심: 필터를 많이 쓸 거면 payload index를 먼저 설계하는 게 중요
# -------------------------
q = QdrantClient(url="http://localhost:6333")  # docker로 qdrant 실행 가정
q.recreate_collection(
    collection_name="demo",
    vectors_config=qm.VectorParams(size=DIM, distance=qm.Distance.COSINE),
)
# tenant 필터 성능을 위해 payload index 생성(문서에서 권장)
q.create_payload_index(
    collection_name="demo",
    field_name="tenant",
    field_schema=qm.PayloadSchemaType.KEYWORD,
)

q.upsert(
    collection_name="demo",
    points=[
        qm.PointStruct(
            id=d["id"],
            vector=d["vec"],
            payload={"text": d["text"], "tenant": d["tenant"]},
        )
        for d in docs
    ],
)

res = q.search(
    collection_name="demo",
    query_vector=query_vec,
    limit=2,
    query_filter=qm.Filter(
        must=[qm.FieldCondition(key="tenant", match=qm.MatchValue(value="t1"))]
    ),
)
print("[Qdrant]", [(r.id, r.score, r.payload["text"]) for r in res])


# -------------------------
# 2) Weaviate: hybrid/압축 옵션이 강점이지만 여기선 기본 vector + filter만
# -------------------------
w = weaviate.connect_to_local()  # docker로 weaviate 실행 가정

try:
    w.collections.delete("Demo")
except Exception:
    pass

coll = w.collections.create(
    name="Demo",
    properties=[
        wc.config.Property(name="text", data_type=wc.config.DataType.TEXT),
        wc.config.Property(name="tenant", data_type=wc.config.DataType.TEXT),
    ],
    # 운영 단계에선 여기서 quantizer(PQ/RQ/BQ 등)까지 함께 설계하는 게 포인트
)

with coll.batch.dynamic() as batch:
    for d in docs:
        batch.add_object(
            properties={"text": d["text"], "tenant": d["tenant"]},
            vector=d["vec"],  # 외부 임베딩 주입
        )

resp = coll.query.near_vector(
    near_vector=query_vec,
    limit=2,
    filters=wc.query.Filter.by_property("tenant").equal("t1"),
    return_properties=["text", "tenant"],
)
print("[Weaviate]", [(o.properties["text"], o.properties["tenant"]) for o in resp.objects])

w.close()


# -------------------------
# 3) Chroma: API 단순, 로컬/임베디드 친화적. (대규모·다중프로세스 운영은 검증 필요)
# -------------------------
ch = chromadb.EphemeralClient()
c = ch.get_or_create_collection("demo")

c.add(
    ids=[d["id"] for d in docs],
    documents=[d["text"] for d in docs],
    embeddings=[d["vec"] for d in docs],
    metadatas=[{"tenant": d["tenant"]} for d in docs],
)

r = c.query(
    query_embeddings=[query_vec],
    n_results=2,
    where={"tenant": "t1"},  # metadata filter
)
print("[Chroma]", list(zip(r["ids"][0], r["documents"][0])))

⚡ 실전 팁

1) “성능 비교”를 볼 때 꼭 p95/p99 + 필터 포함 조건으로 본다

웹에 돌아다니는 벤치마크는 (1) warm cache, (2) 무필터, (3) 단일 노드가 많습니다. 하지만 실무는 tenant 필터 + 시간 범위 + 권한 조건이 들어가며, 이때 filter가 ANN 후보군을 얼마나 줄이느냐가 latency를 좌우합니다. Qdrant가 payload index와 filter cardinality 기반 전략을 강조하는 이유가 여기입니다. (qdrant.tech)

2) 고QPS “항상 켜져 있는” 서비스면 비용 모델이 더 중요해진다

  • Pinecone은 On-Demand 외에 DRN으로 전용 읽기 용량을 예약해 성능/비용 예측을 노립니다. “트래픽이 꾸준한 서비스”일수록 이런 모델이 유리해질 수 있습니다. (pinecone.io)
  • 반대로 트래픽이 들쭉날쭉하면 usage 기반이 더 싸게 나올 때가 많습니다(단, 벤더별 과금 단위 확인 필수).

3) 압축(quantization)은 ‘켜면 끝’이 아니라 “품질 예산”을 설계하는 일

Weaviate의 PQ/RQ, Qdrant의 scalar/binary/product quantization 모두 recall 저하 ↔ 비용/속도의 교환입니다. 중요한 건:

  • 오프라인에서 Recall@K / NDCG 같은 지표로 “허용 가능한 품질 저하”를 수치화
  • rescoring/overfetch 같은 옵션이 있는지 확인(Weaviate는 rescoring을 문서화) (docs.weaviate.io)

4) Chroma는 “팀/서비스 형태”에 맞을 때 압도적으로 빠르다(개발 속도 관점)

Chroma는 API가 단순하고 임베디드/로컬 개발에 강합니다. 다만 PersistentClient 관련 메모리 이슈가 보고된 바 있어(특히 요청마다 새 인스턴스/새 persist_directory 패턴) 프로덕션 패턴에서는 부하 테스트가 필요합니다. (github.com)


🚀 마무리

  • Pinecone: “운영 부담 최소 + 고QPS 예측 가능성”이 목표면 유력(특히 DRN 같은 읽기 전용 용량 모델). (pinecone.io)
  • Weaviate: hybrid search(BM25F+vector)와 RQ/PQ 같은 압축 옵션으로 “품질/비용 최적화”를 적극적으로 하고 싶을 때 강함. (docs.weaviate.io)
  • Qdrant: payload index 중심의 필터 성능/튜닝 투명성이 좋아 “필터가 많은 검색 서비스”에 특히 잘 맞음. (qdrant.tech)
  • Chroma: 빠른 프로토타이핑/임베디드 용도엔 매우 매력적이지만, 장기 운영 패턴(메모리/멀티프로세스/대규모)에서는 사전 검증이 필요. (github.com)

다음 학습으로는 (1) 내 데이터셋에서 필터 포함 p95/p99 측정 스크립트 만들기, (2) quantization별 Recall@K 실험, (3) 트래픽 패턴(steady vs bursty)에 맞춘 과금 모델 시뮬레이션을 추천합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.