Erdmates Logo
Erdmates
ブログ一覧へ
pgvectorRAGPostgreSQLAI

pgvectorでRAG用のベクトルテーブルを設計する:次元、インデックス、フィルタリングまで

PostgreSQLでpgvectorを使って文書の埋め込み(エンベディング)を保存する際のテーブル構造、ベクトル型ごとの次元制限、HNSWインデックス、メタデータフィルタリングの設計を、pgvector 0.8を基準に整理します。

2026年10月12日9 分で読めます

なぜPostgreSQLにベクトルを保存するのか

RAG(検索拡張生成)は、質問と意味の近い文書の断片を探し出し、LLMに一緒に渡す方式です。このとき「意味が近い」ことを計算するために、文書の断片を数値ベクトル(埋め込み/エンベディング)に変換して保存します。

pgvectorは、PostgreSQLでベクトルを保存し、類似度で検索できるようにするオープンソースの拡張機能です(2026年10月1日に0.8.7がリリース)。専用のベクトルデータベースの代わりにPostgreSQLに置けば、文書の権限・テナント・更新日時といった既存のデータと同じトランザクション、同じJOINで扱えるという利点があります。そのぶん、テーブル設計が検索品質とパフォーマンスを左右します。

1. 文書とチャンクを分けた構造

埋め込みは通常、文書全体ではなく、一定の長さに区切った**チャンク(chunk)**単位で作成します。文書とチャンクを1:Nに分けます。

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
  id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  tenant_id   BIGINT      NOT NULL,
  title       TEXT        NOT NULL,
  source_url  TEXT,
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE document_chunks (
  id           BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  document_id  BIGINT NOT NULL REFERENCES documents (id) ON DELETE CASCADE,
  tenant_id    BIGINT NOT NULL,
  chunk_index  INT    NOT NULL,
  content      TEXT   NOT NULL,
  embedding    vector(1536) NOT NULL,
  model        TEXT   NOT NULL,
  embedded_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE (document_id, chunk_index)
);

設計のポイントです。

  • vector(1536) の次元は、埋め込みモデルの出力次元と同じでなければなりません。異なる次元のベクトルを同じカラムに入れることはできません
  • model カラム:異なるモデルが生成したベクトル同士は比較できません。モデルを切り替えるときに、どの行を作り直すべきかをこのカラムで特定します
  • tenant_id をチャンクにも持たせます:検索時にはほぼ常にテナントで絞り込むため、JOINなしですぐにフィルタリングでき、後述する部分インデックス・パーティショニングにも使えます
  • ON DELETE CASCADE:文書を削除するとそのチャンクとベクトルも一緒に削除され、削除した文書が検索に残りません
  • embedded_at と文書の updated_at を比較すれば、原文が変更された後に埋め込みを作り直していないチャンクを見つけられます

2. ベクトル型と次元制限

pgvectorは4つの型を提供しており、保存できる次元とインデックスを作成できる次元が異なります。vector と halfvec は16,000次元まで保存できますが、インデックスの上限はそれより低くなります。

型インデックスを作成できる上限
vector2,000次元
halfvec(半精度)4,000次元
bit(バイナリ)64,000次元
sparsevec(疎ベクトル)非ゼロ要素1,000個(保存は16,000個まで)

埋め込みが2,000次元を超えると、vector カラムにそのままインデックスを作成することはできません。この場合は、半精度に変換した式インデックスを使います。クエリでもインデックスと同じ式で並べ替えなければ、インデックスは使われません。

-- 3072次元の埋め込みを半精度でインデックス化
CREATE INDEX ON document_chunks_large
  USING hnsw ((embedding::halfvec(3072)) halfvec_cosine_ops);

SELECT id, content
FROM document_chunks_large
ORDER BY embedding::halfvec(3072) <=> $1::halfvec(3072)
LIMIT 5;

3. 距離演算子とHNSWインデックス

pgvectorの距離演算子は、<->(L2)、<#>(内積の負値)、<=>(コサイン距離)、<+>(L1)、そしてバイナリベクトル用の <~>(ハミング)、<%>(ジャカード)です。埋め込みモデルが推奨する距離を選び、インデックスの演算子クラス(opclass)とクエリの演算子を合わせる必要があります。インデックスは、ORDER BY が距離演算子の昇順のときに使われます。

インデックスの種類は2つです。

  • HNSW:多層グラフ。検索性能は高いものの、構築が遅く、メモリも多く使います。学習ステップは不要です
  • IVFFlat:ベクトルをリストに分割します。構築が速く、メモリ使用量も少ないものの、検索性能はHNSWより劣ります
-- コサイン距離のHNSWインデックス(デフォルト値: m = 16, ef_construction = 64)
CREATE INDEX ON document_chunks USING hnsw (embedding vector_cosine_ops);

-- 検索時の候補数(デフォルト40)。上げると再現率↑ 速度↓
SET hnsw.ef_search = 100;

SELECT id, document_id, content
FROM document_chunks
WHERE tenant_id = 42
ORDER BY embedding <=> $1
LIMIT 5;

HNSWインデックスは、グラフが maintenance_work_mem に収まるとはるかに速く構築されます。大きなテーブルにインデックスを作成するときは、この値もあわせて検討してください。

4. フィルタリング:結果が足りなくなる問題と解決策

近似インデックスでは、インデックスを先に探索してからフィルターが適用されます。pgvectorのドキュメントの例では、条件に合う行が全体の10%で、hnsw.ef_search がデフォルト値の40の場合、平均4行しか残りません。LIMIT 5 を指定しても、5件に満たない可能性があるということです。

解決策は状況に応じて選びます。

反復インデックススキャン(0.8.0から)。 結果が十分に集まるまで、インデックスをさらに探索します。

SET hnsw.iterative_scan = strict_order;   -- 距離の順序を厳密に維持
-- または
SET hnsw.iterative_scan = relaxed_order;  -- 順序が多少ずれることがあるが、再現率はより高い

フィルター対象のカラムに通常のインデックス。 条件が全行のごく一部しか選ばないのであれば、B-treeインデックスで先に絞り込むほうが有利です。

CREATE INDEX ON document_chunks (tenant_id);

よく使う値が少数なら部分インデックス、値が多ければパーティショニング。

-- 特定テナント専用のベクトルインデックス
CREATE INDEX ON document_chunks USING hnsw (embedding vector_cosine_ops)
  WHERE (tenant_id = 42);

値の種類が多い場合は、PARTITION BY LIST (tenant_id) のようにフィルター対象のカラムでパーティションを分ける方法を、pgvectorのドキュメントが推奨しています。

5. 運用チェックリスト

  • 埋め込みモデルを変更するとき:model カラムで対象を選び、新しいベクトルを別のカラムやテーブルに作成してから切り替えれば、検索が途切れません
  • 原文が変更されたとき:該当する文書のチャンクを削除して作り直すか、embedded_at < updated_at のチャンクを定期的に探して更新します
  • 次元制限の確認:インデックスが必要なカラムは、型ごとのインデックス次元の上限内に収まっている必要があります
  • 検索品質の測定:ef_search・反復スキャンの設定は、精度と速度のトレードオフです。実際のクエリで再現率を測ってから決めてください

Erdmatesで設計する

RAGスキーマの要は、文書–チャンクの1:N関係、削除が連鎖する外部キー、そしてフィルターに使うテナントキーです。Erdmatesでこの関係を先にERDとして描いておけば、ベクトルカラムをどのテーブルに置き、どのカラムで絞り込むかを、チームが同じ図を見ながら決められます。

まとめ

pgvectorの設計で性能と品質を分けるのは、結局のところごく普通のデータベース設計です。文書とチャンクを分け、モデルと更新日時を記録し、次元の上限に合った型を選び、フィルター条件に合ったインデックス戦略(反復スキャン・部分インデックス・パーティショニング)を立てること。この4つを押さえれば、ベクトル検索も既存のデータと同じ水準で管理できます。

Erdmatesで今すぐ始めましょう

AI搭載ERDエディタでデータベース設計を始めましょう。無料でご利用いただけます。

無料デモを試す無料で始める

関連記事

bigint・UUID v4・UUID v7:主キーの選び方(PostgreSQL 18 の uuidv7() 対応)

bigint・UUID v4・UUID v7 の主キーの長所と短所を比べ、PostgreSQL 18 に入った uuidv7() で変わった点と、状況別の選び方を整理します。

AIエージェントが読むデータベース:MCP時代のスキーマ設計原則

AIエージェントがMCPを通じてデータベースに直接接続するケースが増えています。エージェントがスキーマを誤って解釈しないように名前・コメント・制約を設計し、権限を安全に分ける方法を整理します。

PostgreSQL 19プレビュー:スキーマ設計と運用に影響する変更点(ベータ4時点)

正式リリースを控えたPostgreSQL 19の変更点のうち、テーブル設計・マイグレーション・運用に直接影響するものを公式リリースノート(ベータ4)に基づいて整理します。ベータで取り下げられた機能や、アップグレード前に確認すべき動作の変更もあわせて見ていきます。

Erdmates Logo
Erdmates

自然言語でデータベースを設計し、AI駆動ERDエディターでチームとリアルタイムでコラボレーション。

サービス

  • 機能紹介
  • 料金プラン
  • デモ
  • SQL→ERD変換

リソース

  • ブログ
  • ヘルプ
  • お問い合わせ

法的事項

  • 利用規約
  • プライバシーポリシー
  • 第三者提供
会社名: Produx|代表者: 최정회
住所: 강원특별자치도 원주시 솔우물2길 4-1 (무실동)
メール: master@erdmates.com
個人情報管理責任者: 최정회|ホスティングサービス提供者: Amazon Web Services

© 2026 Produx. All rights reserved.