RAG を作るとき、最初の質問はたいてい「ベクトル DB はどれにしますか?」です。私はこれに「場合による」と答え続けてきました。正しいけれど、何の役にも立ちません。そこで、「場合による」の中身を実測データで答える RAG を作ることにしました。rag-db-advisor です。Claude Code から MCP 経由で「10 万件・日本語・更新頻度高めならどの DB?」と聞くと、実測値を根拠つきで返します。
問題は、その「実測データ」でした。既存の比較記事の多くは機能表かベンダー公表値で、検索品質を人手の正解データで測った再現可能な一次データは見つかりませんでした。ないので、作りました。7 製品 9 構成を同一条件で測るハーネス rag-retriever-bench です。対象は pgvector、ClickHouse(3 構成)、Qdrant、Weaviate、Milvus、Chroma、LanceDB。
結論を先に置きます。検索品質はほぼ同着でした。差が出たのはレイテンシの固定費構造、書き込みの速さ、そして運用の罠の数です。特に 3 つ目が強烈で、7 製品中 3 製品が、エラーを一切出さずに劣化する挙動を踏みました。この記事はその一部始終です。
先にゴール: 実測データに答えさせる RAG
完成形から見せます。
claude mcp add rag-db-advisor -- rag-db-advisor mcp
MCP サーバーとして動き、Claude Code や Claude Desktop から DB 選定・チューニングの質問を投げられます。ツールは advise(実測ベースの根拠)、compare_backends(規模別の比較表)、list_traps(踏んだ罠のカタログ)の 3 つ。回答の合成はクライアント側の LLM がやるので、サーバー側に生成用の API キーは要りません。
こだわりは 2 つです。1 つ目、知識源は自分の実測と運用ノートだけ。回答の数値はすべて後述のベンチマークに由来し、出典つきで返ります。ハルシネーションの入り口を構造的に塞ぎました。
2 つ目、検索層はベンチマークの抽象化をそのまま import しています。では advisor 自身はどの DB を選んだかというと、Chroma の embedded です。数百チャンク規模では品質が同着なので、運用が一番軽いものを選ぶ。アドバイザーが自分の助言に従っている構図です。作った本人が言うのも変ですが、ここが一番気に入っています。
あとはこの RAG に食わせる「実測データ」を作るだけです。ここからが本編で、全体の 9 割の時間はこちらに溶けました。
知識源がないなら測る
条件を固定してデータベースだけを差し替えるハーネスを書きました。
- データ: MIRACL(ja) — 日本語 Wikipedia パッセージ + 人手の正解判定。860 クエリ、コーパス 1 万 / 10 万件
- 埋め込み: text-embedding-3-small を全構成共通。10 万件で $0.30(一番高くついたのは埋め込み代ではなく、後述の罠で溶けた時間でした)
- インデックス: HNSW パラメータを全構成で統一。差が出たらエンジンの差で、チューニングの差ではありません
- 指標: recall / nDCG / MRR@10 とレイテンシ p50/p95/p99、取り込み・構築時間。LLM-as-judge は使わないので、何回走らせても同じ意味の数字が出ます
バックエンドは小さなインターフェース(setup / load / build_index / search)の実装だけで追加できます。1 バックエンド 1 ファイルです。
結果 1: 検索品質で DB を選ぶ意味は(ほぼ)ない
10 万件・9 構成の全体像です。
HNSW 勢 7 構成の recall@10 は 0.921〜0.947 に収束しました。1 万件だと 0.976〜0.983 で、さらに団子です。順位表を作るつもりで 9 個測ったら、表彰台に全員乗ってしまいました。「精度が高いベクトル DB はどれか」という比較軸は、実測すると成立しませんでした。
例外は量子化です。
LanceDB の標準インデックスは HNSW に 8bit スカラー量子化を重ねたもので、recall@10 は 1 万件 0.901、10 万件 0.811。素の HNSW 勢との差は 8 ポイントから 12 ポイントへ、スケールで拡大します。見るべきは「HNSW と書いてあるか」ではなく「量子化が入るか」でした。
結果 2: レイテンシはアーキテクチャの固定費で決まる
10 万件の検索 p50 は、専用ベクトル DB 勢(Weaviate 1.8ms / Milvus 2.0ms / Qdrant 3.3ms)が最速帯、pgvector が 4.8ms、ClickHouse はデフォルト設定だと 43.1ms でした。
ClickHouse だけ桁が違う理由は、列指向ならではの読み出し増幅でした。HNSW が返す候補は行ではなく「granule(行のブロック)」で、再スコアリングでブロック丸ごと読みます。index_granularity を 8192 から 128 に絞ると読む行数が 64 分の 1 になり、p50 は 43.1ms → 11.5ms。設定 1 つで 3.7 倍です(recall への影響はラン間ノイズ程度でした)。
結果 3: 書き込み側はしばしば検索より効く
10 万件の取り込み + インデックス構築は ClickHouse 約 20 秒、pgvector 約 56 秒。検索 p50 の数 ms 差より、この 36 秒差のほうが効く設計は珍しくありません。毎晩コーパスを作り直す RAG なら書き込み側を、一度作って読むだけなら検索側を見る。重視すべき軸は更新頻度が決めます。
本題: エラーなしで劣化した 3 つ
ここからが一番書きたかった部分です。構築の過程で、7 製品中 3 製品が「エラーではなく、品質かレイテンシの劣化」として壊れました。
ClickHouse: allow_experimental_vector_similarity_index = 1 はクエリ実行時にも必要で、付け忘れると HNSW のつもりの検索が黙ってフルスキャンになります。発覚したのは「HNSW が 27ms、brute force が 31ms でほぼ同じ」という不自然な数字から。「ClickHouse は HNSW でもこの速さなのか」と結論しかけていたので、危ないところでした。
Qdrant: デフォルトでは 20MB 未満のセグメントを索引化しません。ステータスは green のまま索引ゼロで、860 クエリ全部がフルスキャンでした。この green は「問題ありません」ではなく「やることがありません」の緑です。小さいデータで検証してから本番へ、という常識的な手順が、検証段階だけインデックスなしという罠になります。
Milvus: 2 段構えでした。quick-setup はインデックス指定を黙って捨てて AUTOINDEX を作ります。それを直しても、ロード済みスナップショットが新しいセグメントを含まず、search は「見えている分だけ」から top-10 を返します。recall だけが 0.922 に静かに下がり、全再ロードで 0.980 に戻りました。
共通するのは、失敗がエラーではなく数字の劣化として現れることです。劣化した数字は、比較対象がなければ「そういう性能」に見えてしまいます。
対策として、ハーネスに self_check という機構を入れました。EXPLAIN やサーバー統計で「ANN インデックスが本当に使われているか」を機械検証し、レポートに記録します。ちなみにクエリプランで確認できるのは pgvector と ClickHouse だけで、残りはサーバー統計か設定値まで。プランを見せてくれる DB は、それだけで運用上の価値があります。
ほかにも、1 万件では出ず 10 万件で初めて出る罠(pgvector の並列インデックス構築が要求する 2.1GB の共有メモリ、ClickHouse の挿入時タイムアウト等)をいくつも踏みました。対策込みの罠カタログはリポジトリにまとめてあり、そのまま rag-db-advisor の list_traps の中身になっています。踏んだ罠は、供養すると knowledge base になります。
限界と注意
- 単一ノード Docker での測定です。分散構成・高負荷並列は対象外
- データは MIRACL-ja、埋め込みは text-embedding-3-small。別条件では数字が変わります。変わるからこそ、自分のデータで測るためのハーネスを公開しています
- Chroma / LanceDB は in-process 動作で、サーバー型とレイテンシを直接比較できません(レポートでも表を分けています)
- HNSW には乱数性があり、recall はラン間で ±0.005 程度揺れます。0.01 未満の差から結論を出すのは危険です
まとめ
- 「RAG 選定に答える RAG」を作るために、7 製品 9 構成を同一条件で実測した。データと罠カタログは rag-retriever-bench、答えさせる MCP サーバーは rag-db-advisor にあります
- 同一条件なら HNSW 勢の検索品質はほぼ同着。精度で DB を選ぶ時代は(この規模では)終わっていました。差はレイテンシの固定費、書き込み速度、運用の罠の数に出ます
- 7 製品中 3 製品が「エラーなしの劣化」で壊れた。インデックスが効いているかは、信じずに検証するが対策です
AI 時代のデータベースに何が変わったかと聞かれたら、私はこう答えます。SQL の正しさはエラーが教えてくれましたが、ベクトル検索の正しさは黙って劣化します。データベースを信頼する方法が、宣言から検証に変わりました。その検証装置ごと公開してあるので、この記事の数字は、私の言葉ではなく再実行で疑ってもらえます。
📘 関連Book
- LLMを「嘘つき」から「専門家」に変える技術: Context Engineering 実践入門 -- 検索した結果を LLM にどう渡すか、RAG の後半戦の設計
- MCP実践セキュリティ: 本番導入で躓かないための完全ガイド -- rag-db-advisor のような MCP サーバーを本番で安全に運用する側の話



