OSSベクターデータベース比較:Qdrant vs Weaviate vs Milvus でPineconeを脱却する
オープンソースラボ編集部 ・ 2026年6月13日
OSS ベクターデータベース比較:Qdrant vs Weaviate vs Milvus でPineconeを脱却する
Pinecone(月$70/p1-x1〜)・Weaviate Cloud(月$25/sandboxユニット〜)・Zilliz Cloud(月$65/CU〜)に対して、Qdrant(Rust製・高速・省メモリ)・Weaviate(GraphQL・マルチモーダル・自己完結)・Milvus(大規模・分散設計)はベクター検索をセルフホストできます。
OSSベクターデータベースを使う理由
- コスト: Pinecone p2-x1(月$240/100Mベクター)→ Qdrant + VPS(月$30)
- データ主権: エンベディングベクターを自社サーバーに保持(GDPR・機密情報対応)
- カスタム距離関数: コサイン・ドット積・L2距離を用途に応じて選択・カスタマイズ
- フィルタリング: メタデータフィルター付きベクター検索(Pineconeは無料層では限定的)
主要ツールの概要
Qdrant
2021年公開、Rust製のOSSです。GitHubスター20k+。高速なベクター検索・低メモリ消費・豊富なフィルタリング機能が特長です。HNSW(Hierarchical Navigable Small World)アルゴリズムを採用し、億規模のベクターでも高速なANN(近似最近傍)検索を実現します。
# docker-compose.yml - Qdrant
version: '3.8'
services:
qdrant:
image: qdrant/qdrant:v1.10.0
restart: unless-stopped
ports:
- "6333:6333" # REST API
- "6334:6334" # gRPC
environment:
QDRANT__SERVICE__GRPC_PORT: "6334"
QDRANT__SERVICE__HTTP_PORT: "6333"
QDRANT__STORAGE__ON_DISK_PAYLOAD: "true"
# APIキー認証
QDRANT__SERVICE__API_KEY: ${QDRANT_API_KEY}
volumes:
- qdrant_data:/qdrant/storage
volumes:
qdrant_data:
# Qdrant + Claude API でRAGパイプラインを構築(Python)
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, PointStruct,
Filter, FieldCondition, MatchValue, Range
)
import anthropic
import os
# Qdrantクライアント
qdrant = QdrantClient(
url=os.environ['QDRANT_URL'],
api_key=os.environ['QDRANT_API_KEY'],
)
# Claude API(エンベディング用にはClaudeのAPIではなくOpenAIのtext-embedding-3-smallを使うのが一般的)
# ここではAnthropicのメッセージAPIで推論のみに使用
claude = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])
COLLECTION = 'articles'
VECTOR_DIM = 1536 # text-embedding-3-small
def init_collection():
'''コレクションを初期化(未存在の場合のみ)'''
existing = {c.name for c in qdrant.get_collections().collections}
if COLLECTION not in existing:
qdrant.create_collection(
collection_name=COLLECTION,
vectors_config=VectorParams(size=VECTOR_DIM, distance=Distance.COSINE),
)
# インデックスの最適化(ペイロードフィルター用)
qdrant.create_payload_index(COLLECTION, 'category', 'keyword')
qdrant.create_payload_index(COLLECTION, 'published_at', 'datetime')
print(f'Collection "{COLLECTION}" を作成しました')
else:
print(f'Collection "{COLLECTION}" は既に存在します')
def get_embedding(text: str) -> list:
'''OpenAI APIでエンベディングを生成'''
from openai import OpenAI
oai = OpenAI(api_key=os.environ['OPENAI_API_KEY'])
resp = oai.embeddings.create(input=text, model='text-embedding-3-small')
return resp.data[0].embedding
def upsert_article(article_id: str, title: str, content: str,
category: str, published_at: str):
'''記事をQdrantにインデックス'''
text_to_embed = f'{title}
{content[:2000]}' # 最初の2000文字でエンベディング
vector = get_embedding(text_to_embed)
qdrant.upsert(
collection_name=COLLECTION,
points=[
PointStruct(
id=article_id, # UUIDまたは整数
vector=vector,
payload={
'title': title,
'category': category,
'published_at': published_at,
'content_preview': content[:500],
},
)
],
)
print(f'インデックス完了: {title}')
def semantic_search(query: str, category: str = None,
limit: int = 5) -> list:
'''セマンティック検索(カテゴリフィルター付き)'''
query_vector = get_embedding(query)
# カテゴリフィルター(任意)
filter_condition = None
if category:
filter_condition = Filter(
must=[FieldCondition(key='category', match=MatchValue(value=category))]
)
results = qdrant.search(
collection_name=COLLECTION,
query_vector=query_vector,
query_filter=filter_condition,
limit=limit,
with_payload=True,
score_threshold=0.7, # 類似度0.7未満を除外
)
return [
{'title': r.payload['title'], 'score': r.score,
'category': r.payload['category'], 'preview': r.payload['content_preview']}
for r in results
]
def rag_answer(question: str, category: str = None) -> str:
'''RAG: 検索した記事をコンテキストにしてClaudeが回答'''
relevant_docs = semantic_search(question, category, limit=3)
context = '
'.join([
f'## {d["title"]}
{d["preview"]}' for d in relevant_docs
])
message = claude.messages.create(
model='claude-sonnet-4-6',
max_tokens=1024,
system='''あなたはOSSツールの専門家です。以下の記事コンテキストを参考に質問に答えてください。
コンテキストにない情報は推測せず、「この質問についてはコンテキスト外です」と答えてください。''',
messages=[
{
'role': 'user',
'content': f'コンテキスト:
{context}
質問: {question}',
}
],
)
return message.content[0].text
# 使用例
if __name__ == '__main__':
init_collection()
# 記事を登録
upsert_article(
'550e8400-e29b-41d4-a716-446655440001',
'Keycloak で始めるSSO構築',
'KeycloakはRed Hat製のOSS IDプロバイダです...',
'security', '2026-01-15T00:00:00Z',
)
# セマンティック検索
results = semantic_search('認証の設定方法を教えて', category='security')
for r in results:
print(f'{r["score"]:.3f} {r["title"]}')
# RAG回答
answer = rag_answer('KeycloakのMFAをどう設定しますか?')
print(answer)
Weaviate
2019年公開、Go製のOSSです。GitHubスター11k+。GraphQL APIとモジュールシステムが特長で、text2vec-openai・text2vec-cohere・generative-openaiなどのモジュールを有効化するだけでエンベディング生成・生成AI統合が設定ゼロで使えます。
# docker-compose.yml - Weaviate + OpenAI統合
version: '3.8'
services:
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.25.10
restart: unless-stopped
ports:
- "8080:8080"
- "50051:50051" # gRPC
environment:
QUERY_DEFAULTS_LIMIT: "25"
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "false"
AUTHENTICATION_APIKEY_ENABLED: "true"
AUTHENTICATION_APIKEY_ALLOWED_KEYS: ${WEAVIATE_API_KEY}
AUTHENTICATION_APIKEY_USERS: admin
PERSISTENCE_DATA_PATH: /var/lib/weaviate
# OpenAIモジュール統合
ENABLE_MODULES: "text2vec-openai,generative-openai"
DEFAULT_VECTORIZER_MODULE: "text2vec-openai"
OPENAI_APIKEY: ${OPENAI_API_KEY}
volumes:
- weaviate_data:/var/lib/weaviate
volumes:
weaviate_data:
# Weaviate Python クライアントでスキーマ定義とデータ登録
import weaviate
import weaviate.classes as wvc
import os
client = weaviate.connect_to_local(
port=8080, grpc_port=50051,
auth_credentials=weaviate.auth.AuthApiKey(os.environ['WEAVIATE_API_KEY']),
headers={'X-OpenAI-Api-Key': os.environ['OPENAI_API_KEY']},
)
# コレクション(スキーマ)を作成
if not client.collections.exists('Article'):
client.collections.create(
'Article',
vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(
model='text-embedding-3-small',
),
generative_config=wvc.config.Configure.Generative.openai(
model='gpt-4o-mini',
),
properties=[
wvc.config.Property(name='title', data_type=wvc.config.DataType.TEXT),
wvc.config.Property(name='content', data_type=wvc.config.DataType.TEXT),
wvc.config.Property(name='category', data_type=wvc.config.DataType.TEXT,
tokenization=wvc.config.Tokenization.FIELD),
],
)
# セマンティック検索(nearText)
articles = client.collections.get('Article')
results = articles.query.near_text(
query='認証セキュリティの設定',
filters=wvc.query.Filter.by_property('category').equal('security'),
limit=5,
return_properties=['title', 'category'],
return_metadata=wvc.query.MetadataQuery(score=True),
)
for r in results.objects:
print(f'{r.metadata.score:.3f} {r.properties["title"]}')
client.close()
機能比較表
| 比較項目 | Qdrant | Weaviate | Milvus |
|---|---|---|---|
| 言語 | Rust | Go | Go/C++ |
| GraphQL API | ❌ | ✅ | ❌ |
| メモリ効率 | ★★★ | ★★☆ | ★★☆ |
| 分散スケーリング | ✅ | ✅ | ✅(専用設計) |
| 埋め込み自動生成 | ❌(外部API必要) | ✅(モジュール) | ❌ |
| GitHub Stars | 20k+ | 11k+ | 30k+ |
OSSベクターデータベースはLLM Toolsカテゴリ/categories/llm-toolsのLangChain・LlamaIndex・HaystackのRAGフレームワークとネイティブ統合して意味検索付きチャットボットを構築します。DevOpsカテゴリ/categories/devopsのObservabilityスタックでQdrantのクエリレイテンシ・スループットをPrometheusでモニタリングして検索品質をトラッキングします。
FAQ
Q. PineconeからQdrantへの移行手順を教えてください。
A. ①Pineconeのデータをエクスポート: Pinecone APIでベクターをバッチ取得(index.fetch(ids=[...]))。100万件以上ある場合はIDリストをページネーションして順次取得②Qdrantコレクションを作成: qdrant_client.create_collection()でPineconeと同じ次元数・距離関数を指定③バッチインポート: qdrant_client.upload_points()で1000件ずつバッチ処理(Qdrantは並列upsertが高速)④SDKの切り替え: pinecone-client→qdrant-clientでクエリAPIを更新(index.query()→client.search())。主な差異: Pineconeはnamespaceでデータを分離するがQdrantはcollectionが対応。Pineconeのmetadata filterはQdrantのFilterオブジェクトに対応。移行後のパフォーマンス確認: ベンチマーク検索で上位K件の一致率(Recall@K)を測定して精度の退行がないか確認します。
Q. Qdrant・Weaviate・Milvusのどれを選ぶべきですか?
A. リソース効率重視・シンプルAPI → Qdrant。モジュール統合・GraphQL・マルチモーダル → Weaviate。億規模以上の分散設計 → Milvusが基本指針です。具体的な判断: ①チームが小さく(〜5名)、RAGチャットボット・意味検索を素早く構築したい → Qdrant(Rustで高速、Python APIがシンプル)②OpenAI/Cohere/HuggingFaceのモデルをゼロ設定で統合したい、GraphQLが好き → Weaviate(text2vec-openaiモジュールで自動ベクトル化)③数十億件以上のベクター・分散クラスター・専任インフラチームがいる → Milvus(Kubernetes専用設計)。RAGプロトタイプならQdrant(Docker 1コンテナで起動)から始めて、必要に応じて移行するのが最もコスト効率がよいです。
Q. ベクターデータベースのインデックスアルゴリズム(HNSW・IVF)はどう違いますか?
A. ①HNSW(Hierarchical Navigable Small World): QdrantとWeaviateが採用。グラフ構造でANN検索を実現。メリット: 高Recall(精度)、インデックス作成後の追加・削除が高速。デメリット: インデックスのメモリ使用量が大きい(ベクター次元×8バイト×N×接続数)②IVF(Inverted File Index): FaissやMilvusで選択可。ベクターをクラスターに分割してクラスター内を線形探索。メリット: メモリ効率が良い(大規模向け)。デメリット: インクリメンタル更新が苦手③HNSW + Quantization: QdrantのScalar Quantization(SQ)・Product Quantization(PQ)でHNSWのメモリを1/4〜1/32に削減。1億ベクター(1536次元)のフルサイズ: 約600GB → SQで約150GB → PQで約19GB。実用的なスペック目安: 10万ベクター(1536次元) → RAM 2GB、100万ベクター → RAM 10GB(HNSW + SQ)。
Q. ハイブリッド検索(セマンティック+キーワード)はどう実装しますか?
A. ①Qdrantのハイブリッド検索: sparse_vectorsでBM25のスパースベクターとHNSWのデンスベクターを組み合わせ(v1.7+)。Reciprocal Rank Fusion(RRF)またはスコアの重み付け加算でランキングを統合②Weaviateのハイブリッド検索: .query.hybrid(query="...", alpha=0.7)でalpha=1.0がセマンティック検索のみ、alpha=0.0がBM25のみ、alpha=0.7が7:3でセマンティック寄りのブレンド③実装例(Qdrant): 日本語のBM25はbm25-encoderライブラリでスパースベクターを生成してQdrantにvectors={'text': dense, 'sparse': sparse}として保存。セマンティック検索のみのケースでは「高精度だが語彙の完全一致が弱い」ため、製品名・人名・技術用語の完全一致がある検索にはハイブリッドが有効です。
まとめ
| ユースケース | 推奨ツール |
|---|---|
| RAGチャットボット・シンプルAPI・省メモリ | Qdrant |
| OpenAI統合・GraphQL・マルチモーダル | Weaviate |
| 億規模・分散クラスター・大規模本番 | Milvus |