「ベクトル検索を導入すれば、ユーザーの意図を汲み取った素晴らしい検索体験が作れる」
そう信じてRAG(検索拡張生成)やセマンティック検索のプロジェクトを立ち上げたものの、いざ本番規模のデータを流し込んだ瞬間に、レイテンシの悪化やサーバーリソースの枯渇に直面したことはありませんか?
実務の現場でも、こうした壁に直面するケースは少なくありません。
特に、現在ベクトルデータベースのインデックス手法としてデファクトスタンダードになりつつあるHNSW(Hierarchical Navigable Small World)。このアルゴリズムは、驚異的な検索速度と高い再現率(Recall)を誇る一方で、サーバーのメモリを大量に消費する性質を持っています。
教科書的なドキュメントやライブラリのREADMEには「高速で高精度」と書かれていますが、その裏にある「コストとの戦い」や「リソース管理の難しさ」については、あまり語られていません。
今回は、大規模ECサイトの検索基盤刷新などにおいて、1000万件規模のデータに対するHNSW導入時に直面しやすい課題と対策について解説します。メモリ枯渇によるシステムダウンのリスクから始まり、パラメータを調整して「速度・精度・コスト」のトライアングルをどう攻略するのか。その実践的なプロセスを、具体的な数値を交えて共有します。
もし検索システムの応答速度改善や、増え続けるインフラコストに頭を抱えているなら、この最適化の記録がきっと役に立つはずです。
課題背景:大規模ECサイトにおける「3秒の壁」
大規模ECプラットフォームの開発において、検索機能のパフォーマンスはユーザー体験、ひいては売上に直結する極めて重要な要素です。ここでは、取扱商品数が1000万点を超える規模のシステムを想定し、技術者が直面しがちな課題と要件を整理します。
1000万点の商品データと意味検索の導入
現代の検索システムに求められるミッションは、従来のキーワード検索では拾いきれない「潜在的なニーズ」を的確に捉えることです。例えば、「キャンプで使う座り心地の良い椅子」と検索されたとき、商品名に「アウトドアチェア」や「折りたたみ椅子」といったキーワードが含まれていなくても、レビュー内容や商品説明の文脈(セマンティクス)から適切な商品をヒットさせたいという要求があります。
これを実現するための標準的なアプローチとして、OpenAIの最新Embeddingモデルなどを用いて商品データをベクトル化し、意味検索(セマンティック検索)を実装する方法が広く採用されています。
仮に1000万件の商品データに対し、それぞれ1536次元(高精度モデルの一般的な次元数)のベクトルを生成するとしましょう。単純計算でも、ベクトルデータだけで数十ギガバイトの規模になります(1536次元 × 4バイト/float × 1000万件 ≈ 約61GB)。これらをデータベースに格納し、ユーザーのクエリベクトルと類似する商品を瞬時に探し出すには、高度な検索基盤が必要となります。
リニアスキャンによるレイテンシ増大の課題
データ量が数万件程度の初期フェーズやPoC(概念実証)段階では、単純な全探索(フラットインデックス / Brute-force Search)でもシステムは問題なく動作します。全探索とは、クエリベクトルと全ての商品ベクトルの距離(コサイン類似度など)を一つずつ計算して比較する方法です。全てのデータを洗うため、精度は100%(完全な最近傍)が保証されます。
しかし、データ量が100万件を超え、1000万件規模に達すると、この手法は計算リソースの限界を迎えます。
検索ボタンを押してから結果が表示されるまで数秒を要し、同時アクセスが増えるピークタイムにはCPU使用率が100%に張り付き、さらなる遅延を引き起こすケースも珍しくありません。ECサイトにおいて「数秒の待機時間」は致命的です。一般的な調査事例にもある通り、表示速度が0.1秒遅れるだけで売上は1%下がると言われており、ユーザー体験を損なう遅延は許容されません。
ビジネス要件:検索時間を200ms以内に短縮する
実運用レベルのビジネスサイドから求められる要件は、明確かつシビアなものになる傾向があります。
- 検索レイテンシ: 99パーセンタイル値(P99)で200ミリ秒以内
- 検索精度(Recall@10): 0.95以上(トップ10件のうち9.5件は、全探索時の正解トップ10に含まれること)
- インフラコスト: 既存予算の範囲内、もしくは費用対効果が見合う微増まで
「精度を落とさず、速度を10倍以上にし、コストは抑える」という、一見すると矛盾するような要求です。こうした課題に直面した際、エンジニアは全探索のアプローチを見直し、近似最近傍探索(ANN: Approximate Nearest Neighbor)アルゴリズムの導入を検討することになります。これは、わずかな精度のトレードオフを受け入れる代わりに、劇的な速度向上を実現する現実的な解となります。
アルゴリズム選定:なぜIVFではなくHNSWを選んだのか
ANN(近似最近傍探索)アルゴリズムにはいくつかの主要な手法があります。一般的に検討されるのは、IVF(Inverted File / 転置インデックス)とHNSW(Hierarchical Navigable Small World)、そしてMicrosoftが開発したDiskANNです。
候補となったインデックス手法(IVF, HNSW, DiskANN)の比較
一般的に、ベクトル検索のインデックス手法を選ぶ際は、以下の3つの指標のバランスを見ます。
- 検索速度(レイテンシ / QPS)
- 検索精度(Recall)
- メモリ効率(コスト)
IVF(転置インデックス)は、ベクトル空間をいくつかの「クラスタ(ボロノイ領域)」に分割し、クエリに近いクラスタだけを探索する手法です。Faissなどで有名ですね。この手法の最大のメリットはメモリ効率が良いこと。インデックスサイズは元データに対してそれほど大きくなりません。しかし、探索するクラスタの数(nprobe)を減らせば速くなりますが精度が落ち、増やせば精度は上がりますが遅くなるという、線形に近いトレードオフがあります。
一方、HNSWは、データをグラフ構造(ノードとエッジ)で管理します。正確には、「高速道路」のような粗い階層と、「一般道」のような細かい階層を持つ多層構造です。上層で大まかな位置を特定し、下層へ降りながら目的地(最近傍)に近づいていきます。グラフを辿るだけなので計算コストが低く、非常に高速です。
DiskANNは、SSDなどのストレージを活用してメモリ使用量を抑える技術です。現在ではMilvusやAzure AI Searchなどのプラットフォームでもディスクベースの最適化やHNSWの改良が進んでいますが、特定のインフラ環境(マネージドKubernetes上のステートフルセットなど)では、ストレージI/Oのボトルネック懸念があり、導入ハードルが高くなる場合があります。
再現率(Recall)とQPS(Queries Per Second)のベンチマーク検証
1000万件のデータを用いたベンチマーク検証では、以下のような特徴的な傾向が見られます。
- IVF: Recall 0.95を出そうとすると、多くのクラスタ(
nprobe)を探索する必要があり、QPS(1秒あたりのクエリ処理数)が伸び悩みます。特に高負荷時にはCPUキャッシュミスが増え、レイテンシのばらつき(ジッター)が目立ちます。 - HNSW: 非常に高いQPSを維持しながらRecall 0.98以上を安定して記録する傾向があります。レイテンシも数ミリ秒〜数十ミリ秒で安定しています。MalkovとYashuninによる原論文(2016年)でも示されている通り、高次元データにおけるHNSWの性能は圧倒的です。
決定打となった「安定したレイテンシ」と「更新頻度への強さ」
ECサイトの特性として、「商品の在庫切れ」「新商品の追加」「価格改定」など、データの更新が頻繁に発生します。
IVFは、データの分布が大きく変わるとクラスタの中心点(セントロイド)が最適でなくなり、再学習(リトレーニング)が必要になることがあります。再学習中は検索を止めたり、ダブルバッファを持ったりする必要があり、運用が煩雑です。
対してHNSWは、グラフ構造に対して動的にノードを追加・削除することに比較的強く、再構築なしでも一定期間は運用可能です。インクリメンタルな更新に強いという点は、リアルタイム性が求められるECサイトにおいて大きなアドバンテージとなります。
また、HNSWは現在も主要なベクトル検索プラットフォーム(Milvusの最新バージョンやAzure AI Searchなど)においてコアアルゴリズムとして採用され続けており、クエリ処理の最適化やパラメータ調整機能(ef_searchの動的調整など)の進化が続いています。長期的な技術スタックとしての安定性も、選定を後押しする要因です。
「速度こそ正義」というビジネス要件と、更新への耐性を考慮すると、HNSWの採用が有力な選択肢となります。しかし、導入にあたっては、HNSWが抱える「メモリ消費量の多さ」という側面に十分な注意を払う必要があります。
実装の壁:メモリ消費量の爆発とチューニングの泥沼
HNSWを本番環境に近いステージング環境へデプロイした際、多くの開発チームが直面するのが無情なアラート通知です。
デフォルト設定での運用失敗パターン
デプロイから数分後、Kubernetesクラスタから OOMKilled (Out of Memoryによりプロセス強制終了)の通知が飛び交うケースは珍しくありません。検索サービス(Pod)が立ち上がったそばから落ちていく現象です。
原因の多くは、HNSWインデックスによるメモリの枯渇です。例えば1000万件規模のベクトルデータを扱う場合、データそのもののサイズに加え、HNSWが構築するグラフ構造のメタデータが想定をはるかに超えるメモリを消費します。ベクトルデータ自体が約60GBであっても、インデックスを含めるとメモリ使用量は100GBを超えることも稀ではありません。
サーバーインスタンスのメモリが64GB程度では全く足りず、「インデックスを貼るだけでデータサイズが倍近くになる」という事実に直面し、設計の見直しを迫られることになります。
また、頻発するガベージコレクション(GC)もパフォーマンス悪化の要因です。メモリに余裕がない状態ではGCが頻繁に走り、その都度すべての処理が止まる「Stop the World」が発生します。高速化のために導入したHNSWが、リソース不足により逆にシステムを不安定にさせてしまう典型的な失敗パターンです。
なお、Kubernetesの最新環境(v1.35など)ではリソース管理機能が強化されていますが、物理的なメモリ不足をソフトウェア側だけで解決することはできません。適切なサイジングとパラメータ設定が不可欠です。
HNSWの構造とメモリ消費のメカニズム解説
なぜこれほどメモリを消費するのでしょうか。技術的な深掘りをします。
通常のリスト構造ならデータ本体だけがあれば済みますが、HNSW (Hierarchical Navigable Small World) は「各データ(ノード)が、他のどのデータと繋がっているか」というリンク情報(エッジ)を大量に保持する必要があります。
HNSWのメモリ消費量は、概算で以下の要素によって決まります。
- 生データ(ベクトル): 次元数 × 4バイト(float32) × データ数
- グラフのエッジ情報: データ数 × M(各ノードの最大エッジ数) × 2(双方向リンクなど)× 4バイト(ポインタID)
- 階層構造の管理コスト: 上位レイヤー(Layer 1, Layer 2...)に存在するノード情報など
特に影響が大きいのが、パラメータ M です。これは「1つのノードから最大何本の線を引くか(近傍ノード数)」を決める値です。M が大きければ大きいほど、グラフの繋がりが密になり、どこからでも目的地にたどり着きやすくなるため、検索精度(Recall)は上がります。しかし、その分だけエッジ情報が増え、メモリ消費量はリニアに増大します。
多くのベクトルDBやライブラリ(Faiss, Hnswlibなど)では、デフォルトで M=48 や M=64 といったリッチな値が設定されていることがあり、これがOOMの主犯となるケースが多いのです。
パラメータ(M, ef_construction)がグラフ構造に与える影響
もう一つの重要なパラメータが ef_construction です。これはインデックスを構築する際に、「どれだけ深く探索して、最適な近傍(エッジの接続先)を探すか」を指定する値です。
- M: グラフの「密度の濃さ」。検索速度と精度、そしてメモリ量に直結します。一度作られたら固定される「道路の車線数」のようなものです。
- ef_construction: グラフの「品質の高さ」。構築時間に影響しますが、検索時のメモリ消費量には直接影響しません(構築時の一時メモリは消費します)。時間をかけて丁寧に道路を敷くかどうか、という指標です。
この2つのパラメータの関係性を理解し、トレードオフを最適化するアプローチが求められます。つまり、「車線数(M)は減らして土地(メモリ)を節約するが、道路工事(ef_construction)は時間をかけて丁寧に行い、渋滞しない高品質な道路を作る」という戦略が有効です。
最新の最適化トレンド:
2026年現在、MilvusやAzure AI Searchなどの主要プラットフォームでは、HNSWの実装最適化が進んでいます。
- Milvus(最新版): クエリ処理やキャッシュの最適化により、HNSWの検索パフォーマンスが向上しています。また、構造化データのサポート拡大により、より柔軟なインデックス設計が可能になっています。
- Azure AI Search:
oversamplingパラメータやExhaustiveモードなどが導入され、精度とレイテンシのバランスをより細かく制御できるようになっています。 - ディスクベースへの移行: メモリ制約を回避するため、オンメモリのHNSWではなく、SSDを活用する DiskANN やその派生技術(DiskBBQ等)への移行も有力な選択肢となっています。
専門家の視点から言えば、単にハードウェアを増強する前に、まずは M 値の見直しと、プラットフォーム固有の最適化オプション(量子化やディスクオフロード)の検討を強く推奨します。
最適化プロセス:トレードオフを攻略する実験記録
ここが技術的な工夫のしどころです。「メモリを減らしつつ、精度は維持する」。この一見矛盾する要求を満たすためには、パラメータチューニングの深い理解と戦略的な検証が不可欠です。
実験設定:パラメータの組み合わせマトリクス
最適化にあたり、検証すべき仮説は以下の通りです。
「M(エッジ数)を減らしてメモリを節約しても、ef_construction(構築時の探索深度)を極端に上げれば、グラフの品質が上がり、精度低下をカバーできるのではないか?」
検証に使用するパラメータセットの例を挙げます。ここではQdrantを例にしていますが、これらはHNSWアルゴリズムの核心となるパラメータであり、他のベクトルデータベースを使用する場合でも、同様の概念(パラメータ名は異なる場合があります)で調整可能です。
- M: 16, 24, 32, 48, 64
- ef_construction: 100, 200, 500, 800
- ef_search(検索時の探索深さ): アプリケーション側で動的に調整
Recall 95%を維持するギリギリのラインを探る
検証データからは、非常に興味深い傾向が見て取れます。
まず、M=48(デフォルトに近い値)ではメモリ消費が大きく、リソース制約のある環境では厳しいケースがあります。
次に M=16 まで下げると、メモリ使用量は劇的に減少します。しかし、ef_construction=100 のままでは、Recall(再現率)が0.85程度まで落ち込み、多くの本番環境で求められる0.95の基準を満たせません。
そこで、仮説通り M=16 のまま ef_construction を 500 まで上げてインデックスを再構築するアプローチが有効です。構築時間は通常の3倍近くかかりますが、検証結果として Recallが0.94まで回復 する傾向があります。
さらに、M=24、ef_construction=400 という組み合わせを試すと、メモリ使用量は許容範囲内(サーバー容量の80%程度)に収まりつつ、Recall 0.96 を達成するケースが多く見られます。これがバランスの取れた「スイートスポット」と言えるでしょう。
導き出される法則:
「メモリが厳しいならMを削る。その代わり、構築時間を犠牲にしてでも ef_construction を高く設定する」
構築時間は、サービス運用中のレイテンシには影響しません(バックグラウンドや夜間バッチで処理するため)。ここを計算コストとして支払うことで、ランニングコスト(メモリ)を節約するという戦略です。
ef_search(検索時の探索深さ)の動的調整の実装
インデックス構築後の「検索時」にも重要な調整ポイントがあります。それが ef_search です。これは検索クエリごとに指定できるパラメータで、検索時にグラフをどれだけ広く探索するかを決定します。
ef_searchを大きくする → より広く探索する(精度UP、速度DOWN)ef_searchを小さくする → 探索を早めに切り上げる(精度DOWN、速度UP)
この性質を利用し、アプリケーション側で動的な制御(Adaptive Query Configuration)を実装するのがベストプラクティスです。
具体的には、APIゲートウェイ層で現在のシステム負荷(CPU使用率やリクエストキューの長さ)を監視し、検索パラメータを動的に書き換えるミドルウェアを配置する手法があります。
- 通常時(CPU < 60%):
ef_search=100で最高精度の検索を提供。 - 混雑時(60% < CPU < 80%):
ef_search=80に調整。 - 高負荷時(CPU > 80%):
ef_search=40に下げてレスポンス速度を優先し、システムダウン(タイムアウト)を防ぐ。
高負荷時に多少精度が落ちたとしても、ユーザーに「エラー」や「長い待機時間」を見せるよりは、高速に「十分実用的な結果」を返す方がUX(ユーザー体験)としては優れています。このような実装により、スパイクアクセス時でも安定した稼働が可能になります。
最終成果とエンジニアへの提言
長いチューニングのプロセスを経て、検索システムのパフォーマンスは大きく向上します。ここでは、HNSWの最適化によって一般的に期待できる成果の目安と、長期運用を見据えたエンジニアへの提言をまとめます。
Before/After比較:レイテンシ25ms達成とメモリ使用量の適正化
適切なパラメータチューニングとインフラ選定を行うことで、以下のような劇的なパフォーマンス改善が期待できます。
- 検索レイテンシ: 全探索(リニアスキャン)と比較して約100倍の高速化(例:数秒から数十ミリ秒オーダーへ)が可能になります。
- スループット(QPS): インデックス構造の効率化により、800 req/sec以上の高負荷時でも安定したレスポンスを維持できるケースが多く報告されています。
- 検索精度(Recall):
efパラメータの調整により、ビジネス要件を満たす高い再現率(例:Recall@10 > 0.95)を確保できます。 - メモリ効率:
M値の適正化や量子化技術の活用により、限られたサーバーリソース(例:64GBメモリ)内で数千万規模のベクトルデータを安定して運用することが可能になります。
こうしたバックエンドの改善は、ユーザー体験(UX)の向上に直結し、検索待ちによる離脱率の低下に大きく寄与します。
運用フェーズでの注意点(データの追加・削除とグラフ品質の劣化)
HNSWは万能ではありません。導入後の運用フェーズにおいて、特に注意すべき課題があります。
最も警戒すべきは「削除によるグラフ構造の劣化」です。データの追加と削除を頻繁に繰り返すと、HNSWのグラフ構造内に孤立したノードが発生したり、エッジの接続が悪くなったりすることがあります。これにより「Navigable(探索可能)」な性質が損なわれ、徐々に検索精度や速度が低下する現象が発生します。
これに対する有効な対策は以下の通りです。
- 定期的な再構築(Rebuild): 週次や月次など、サービスへの影響が少ない時間帯にインデックスを完全再構築するバッチ処理を組み込むことが推奨されます。
- 最新実装の活用: Milvusの最新バージョンやAzure AI Searchなどのプラットフォームでは、HNSWの実装が継続的に改良されています。例えば、検索結果のハイライト表示やクエリ処理の最適化により、グラフ劣化の影響を軽減する機能が含まれている場合があります。
- ディスクベース手法の検討: メモリ制約が厳しい場合、すべてのインデックスをメモリに載せるのではなく、SSDを活用するディスクベースの手法(DiskANNやDiskBBQの概念を取り入れた実装)への移行も検討に値します。
これからHNSWを導入するチームへのチェックリスト
最後に、これからベクトル検索の高速化に取り組む皆さんへ、多くのプロジェクトで実証されたチェックリストを提示します。
- メモリ容量の見積もりは余裕を持つ: 生データの1.5倍〜2倍のメモリを見込むか、量子化(Quantization)機能をサポートするDBを選定してください。
- Mとef_constructionは反比例させる: メモリリソースが厳しい場合は
M(ノード接続数)を下げ、その分ef_constructionを上げてインデックス品質を担保するというトレードオフを意識してください。 - 動的パラメータを活用せよ: 検索時のパラメータ
ef_searchは固定値にする必要はありません。システム負荷や要求精度に応じて動的に調整する設計が、最新のベストプラクティスです。Azure AI Searchなどのマネージドサービスでは、これを自動化するオプション(oversampling等)が提供されている場合もあります。 - 再構築計画を立てよ: 永久に使い続けられるインデックスはありません。定期的なリフレッシュ運用を設計段階で盛り込んでおくことが、長期的な安定稼働の鍵です。
まとめ
HNSWは、その強力さゆえに制御が難しいアルゴリズムです。しかし、内部構造を理解し、パラメータの意味を把握すれば、限られたリソースの中で最大限のパフォーマンスを引き出すことができます。
「デフォルト設定で動かして終わり」ではなく、泥臭いチューニングと最新技術のキャッチアップこそが、エンジニアの価値を発揮する瞬間です。今回のガイドが、皆さんの検索システムの「3秒の壁」を突破するヒントになれば幸いです。
具体的な実装詳細や、Milvus、Azure AI Search、pgvectorなどの各ツールにおける最新の設定オプションについては、必ず各公式ドキュメントで最新情報を確認してください。
コメント