【超入門&徹底解説】pgvectorとは何か?AI時代のデータベースを初心者にもわかりやすく完全ガイド

database-kv-prograshi(プロぐらし)-kv Database
記事内に広告が含まれていることがあります。

近年、ChatGPTをはじめとする生成AIの爆発的な普及に伴い、エンジニアやテック業界で「ベクトル検索(Vector Search)」や「ベクトルデータベース」という言葉が急速に注目を集めるようになりました。

その中心で、世界中の開発者から圧倒的な支持を集めているツールがpgvector(ピージーベクター)です。

本記事では、

  • 「そもそもベクトル検索って何?」
  • 「pgvectorって何がそんなにすごいの?」
  • 「専用のベクトルDB(Pineconeなど)と何が違うの?」
  • 「実際にどうやって動かすの?」

といった疑問を持つ完全な初心者の方に向けて、専門用語の概念から内部の仕組み、SQLによるハンズオン、Pythonを使ったAI連携、本番運用のチューニングまで、余すところなく徹底的に解説します。

  1. はじめに:AI時代になぜ「pgvector」が必要とされるのか?
  2. 前提知識:そもそも「ベクトル」と「ベクトル検索」とは?
    1. ベクトルとは「意味の数値化」である
    2. キーワード検索とベクトル検索(セマンティック検索)の違い
    3. 2RAG(検索拡張生成)におけるベクトルの役割
  3. pgvectorの正体と、選ばれる圧倒的な理由
    1. pgvectorとは「PostgreSQLの拡張機能」
    2. 専用ベクトルDB vs PostgreSQL + pgvector
    3. pgvectorがもたらす「データ一元化」の破壊的メリット
  4. pgvectorのコア技術を徹底解剖
    1. データ型
    2. 3大「距離指標」の選び方
    3. ベクトルインデックスの仕組み(IVFFlat vs HNSW)
  5. ステップ・バイ・ステップで学ぶpgvectorの基本操作
    1. 環境構築(Dockerで一発起動)
    2. 拡張機能の有効化
    3. テーブル作成とデータ挿入
    4. 類似度検索クエリの実行
    5. HNSWインデックスの作成
  6. 実践編:Python × OpenAI × pgvectorでミニRAGシステムを作る
    1. 全体アーキテクチャ
    2. 準備
    3. 実装コード(保存と検索)
    4. 実行結果の確認
  7. 一歩先を行く!ハイブリッド検索(全文検索+ベクトル検索)
    1. なぜベクトル検索だけでは不十分なのか?
    2. PostgreSQLならではのハイブリッド実装
  8. パフォーマンスを引き出す運用の勘所
    1. メモリ設定の最適化
    2. 8-2. HNSWパラメータのトレードオフ
    3. 主要クラウドでのサポート状況
  9. よくある質問(FAQ)
    1. Q1. 何件くらいのデータ量までpgvectorで耐えられますか?
    2. Q2. ベクトルの正規化(Normalize)は必要ですか?
    3. Q3. 更新(UPDATEやDELETE)が頻繁にあるテーブルでも大丈夫ですか?
  10. まとめ:まずは既存のPostgreSQLに CREATE EXTENSION から始めよう
    1. 本記事の重要ポイントのおさらい

はじめに:AI時代になぜ「pgvector」が必要とされるのか?

これまでのITシステムにおいて、データベースの主な役割は「完全一致」や「条件一致」のデータを素早く取り出すことでした。

  • 「ユーザーIDが 12345 のユーザーを取得する」
  • 「価格が 1,000円以上 5,000円以下 の商品を抽出する」
  • 「商品名に スニーカー という文字列が含まれるものを探す」

しかし、LLM(大規模言語モデル)の登場によって、コンピュータに求められる要求が根本から変わりました。

「新生活にぴったりで、朝の時間を節約できる便利な家電を教えて」

このような人間の曖昧で感覚的なリクエストに対して、従来のSQL(LIKE '%新生活%' など)では適切な回答を返すことができません。

「朝の時間を節約」「便利」という言葉のニュアンスや文脈(意味)をデータベースが理解できないからです。

そこで登場したのが、文章や画像の意味を数値化して扱う「ベクトル検索」です。

そして、世界で最も信頼され、普及しているオープンソースのリレーショナルデータベースである「PostgreSQL」に、ベクトル検索機能をプラグイン感覚で追加できる拡張機能こそが「pgvector」です。

前提知識:そもそも「ベクトル」と「ベクトル検索」とは?

pgvectorを理解するために、まずは基礎となる「ベクトル」と「ベクトル検索」について、数式を使わずに直感的なイメージで掴んでおきましょう。

ベクトルとは「意味の数値化」である

数学におけるベクトルとは、矢印の向きと大きさ、あるいは「複数の数字の並び(リスト)」のことです。

AIの世界では、テキスト、画像、音声などのあらゆるデータを、AIモデル(Embeddingモデル)を通して「数字の羅列(埋め込みベクトル:Embedding)」に変換します。

たとえば、果物の特徴を「甘さ」「酸っぱさ」「赤さ」という3つの軸(3次元)で数値化してみましょう。

リンゴ  : [甘さ: 0.8, 酸っぱさ: 0.5, 赤さ: 0.9]
イチゴ  : [甘さ: 0.7, 酸っぱさ: 0.6, 赤さ: 0.8]
レモン  : [甘さ: 0.1, 酸っぱさ: 0.9, 赤さ: 0.0]

この3つの数値を3次元空間上の座標としてプロットすると、「リンゴ」と「イチゴ」の座標は非常に近くに位置し、「レモン」は離れた場所に位置することが直感的にわかります。

実際のAI(例えばOpenAIの text-embedding-3-small など)では、この軸が3個ではなく、1,536個(1,536次元)もの膨大な特徴量として表現されます。言葉のニュアンス、品詞、文脈、トピックなどが1,536個の数字に圧縮されて格納されるのです。

キーワード検索とベクトル検索(セマンティック検索)の違い

従来のキーワード検索と、ベクトルを使った検索(セマンティック検索=意味検索)の違いを比較してみましょう。

比較項目従来のキーワード検索(LIKE, 全文検索)ベクトル検索(セマンティック検索)
検索のアプローチ入力された「文字」が一致するか入力された「意味・文脈」が近いか
例:「犬の散歩」で検索「犬の散歩のコツ」はヒットするが、「ワンちゃんのウォーキング」はヒットしない「ワンちゃんのウォーキング」「ペットと公園へお出かけ」も意味が近いためヒットする
強み型番、固有名詞、エラーコードの完全一致に強い言い換え、表記揺れ、多言語間の意味理解に強い
弱点同義語や言い換えに対応できない(シノニム辞書が必要)完全に特定の英数字・型番をピンポイントで当てるのが苦手な場合がある

ベクトル検索では、検索クエリ(例:「お腹が空いた」)も同じようにベクトル化し、空間上でその座標と距離が近いデータ(例:「おすすめのラーメン屋」「簡単パスタレシピ」)を数学的な計算で探し出します。

【空間のイメージ】

       (高) ↑
            │   [猫]        [犬]  ← 距離が近い(動物・ペット)
            │
  動物っぽさ │
            │
            │           [パソコン] ← 距離が遠い(電子機器)
            └─────────────────────────→ (高)
                         機械っぽさ

2RAG(検索拡張生成)におけるベクトルの役割

現在、多くの企業がLLMを活用した「社内文書チャットボット」や「ナレッジ検索」を構築しています。その標準的なアーキテクチャがRAG(Retrieval-Augmented Generation:検索拡張生成)です。

RAGの流れは次の通りです。

  1. 蓄積:社内マニュアルや規程集を適度な長さに分割し、ベクトル化してデータベースに保存する。
  2. 検索:ユーザーが「出張旅費の申請期限はいつまで?」と質問したら、その質問をベクトル化し、保存されている文章の中から意味の近い社内規程の段落を上位数件検索する。
  3. 生成:検索された該当規程をプロンプトに添付し、LLMに「以下の規程を元に質問に答えてください」と指示する。

この「ステップ2:意味の近い文書を素早く検索する」という心臓部を担うのが、pgvectorをはじめとするベクトルデータベースです。

pgvectorの正体と、選ばれる圧倒的な理由

pgvectorとは「PostgreSQLの拡張機能」

pgvectorは、オープンソースのリレーショナルデータベース「PostgreSQL」上で、ベクトルデータの保存、インデックス作成、および類似度検索を可能にするオープンソースの拡張機能(C言語製)です。

PostgreSQLには元々、GIS(地理情報)を扱う「PostGIS」や、全文検索を高速化する「pg_trgm」など、世界中の開発者が機能を拡張できる優れた仕組みが備わっています。

pgvectorもその仕組みに則り、PostgreSQLの内部エンジンに直接組み込まれる形で動作します。

専用ベクトルDB vs PostgreSQL + pgvector

ベクトル検索が注目され始めた当初、Pinecone、Milvus、Qdrant、Chromaといった「ベクトル検索専用のデータベース(Vector Native DB)」が多く登場しました。

これらは非常に高性能ですが、実際のWebアプリケーション開発の現場では、「PostgreSQL + pgvector」を選ぶケースが爆発的に増えています。

なぜでしょうか? 両者の違いを比較してみましょう。

比較項目専用ベクトルDB(Pinecone, Milvus等)PostgreSQL + pgvector
アーキテクチャベクトル専用の独立したシステム既存のRDBMS(PostgreSQL)に相乗り
データの整合性・ACID限定的(結果整合性が多い)完全なACIDトランザクションをサポート
リレーショナルデータとの結合困難(外部キー結合ができないためアプリ側で結合)通常のSQLでテーブル同士をJOIN可能
運用の複雑さDBが2重管理(PostgreSQLと専用DB)になり同期が必要単一のPostgreSQLのみで運用完結
コスト専用DBの月額費用やクラスタ運用費が別途発生既存のDBサーバーのリソースを活用可能
数億件規模の大規模性能極めて高い(専用にスケールアウト設計)数百万〜数千万件は極めて高速だが、数億件超ではメモリ設計がシビア

pgvectorがもたらす「データ一元化」の破壊的メリット

専用ベクトルDBを採用した場合、多くの開発者がデータの二重管理問題に直面します。


【専用ベクトルDBを使った場合の複雑な構成】

 [ユーザー] ──→ [Webアプリ] ─── (メタデータ/ユーザー情報) ──→ [PostgreSQL]
                     │
                     └───────── (ベクトルデータ) ────────────→ [専用ベクトルDB]

ユーザーが投稿を削除した際、PostgreSQLのレコードを削除し、同時に専用ベクトルDBからも該当のIDを削除しなければなりません。

どちらか一方の通信が失敗するとデータの不整合が起き、権限管理(「部署IDがAの社員にのみ見せてよい文書」など)の実装も複雑を極めます。

しかし、pgvectorを使えば、この問題がすべて解決します。

【pgvectorを採用した場合のシンプルな構成】

 [ユーザー] ──→ [Webアプリ] ─── (ユーザー情報・メタデータ・ベクトル) ──→ [PostgreSQL + pgvector]

PostgreSQLの同一テーブル内に、title(タイトル)、created_at(作成日時)、user_id(所有者)、そしてembedding(ベクトル)をひとまとめに定義できます。


-- 通常のテーブルにvectorカラムを混ぜて定義できる!
SELECT title, content
FROM documents
WHERE user_id = 42 
  AND created_at >= '2026-01-01'
ORDER BY embedding <=> '[0.012, -0.045, ...]' 
LIMIT 5;

このように、「特定のユーザーのデータに絞り込み、特定の日時以降のレコードの中から、意味が近いものを5件取得する」という操作が、たった1本のSQLで、トランザクションの保護を受けながら実行できます。

これこそが、世界中のエンジニアがpgvectorを絶賛している最大の理由です。

pgvectorのコア技術を徹底解剖

ここからは、pgvectorを実務で使う上で絶対に知っておくべき技術的仕様と内部構造について掘り下げていきます。

データ型

pgvectorを有効化すると、PostgreSQLでいくつかの新しいデータ型が利用可能になります。

  1. vector(dim)
    • 標準的な単精度浮動小数点(float4:32bit)の配列。
    • 最大16,000次元まで対応。
    • 一般的なOpenAIの埋め込みモデル(1,536次元や3,072次元)を扱う場合は基本的にこの型を使います。
  2. halfvec(dim)(pgvector 0.7.0以降)
    • 半精度浮動小数点(float2:16bit)の配列。
    • メモリ使用量とインデックスサイズを**約半分(50%削減)**に抑えることができます。大規模データでRAM容量を節約したい場合に強力な選択肢となります。
  3. sparsevec(dim)
    • スパースベクトル(疎ベクトル)を扱うための型。
    • BM25などの語彙的アルゴリズムとベクトルのハイブリッドを行う際、要素のほとんどが0である巨大なベクトルを効率的に保存できます(最大1,000次元の非ゼロ要素を保持)。

3大「距離指標」の選び方

ベクトル空間上で「2つのデータがどれくらい近いか」を測定する計算方法には複数の種類があり、pgvectorはそれぞれに対応する独自の演算子を用意しています。

距離指標演算子概要と使い分け
コサイン距離 (Cosine Distance)<=>【推奨・最も一般的】 矢印の「向きの近さ」を測る(-1〜1を0〜2に変換)。文章の長さの違いに影響されにくいため、自然言語処理・RAGでは原則これを選べば間違いありません。
ユークリッド距離 (L2 Distance)<->2点間の「直線距離(幾何学的距離)」を測る。物理的な座標データや、画像特徴量の一部で利用されます。
内積 (Inner Product / Dot Product)<#>2つのベクトルの掛け算の合計。ベクトルがあらかじめ正規化(長さが1に統一)されている場合、コサイン類似度と数学的に等価になり、最も高速に計算できます(※pgvectorでは最小化のために「負の内積」を返します)。
L1距離 (マンハッタン距離)<+>グリッド状の街並みを移動するように、各軸の差の絶対値を足し合わせた距離。

初心者のための鉄則:
特段の理由がない限り、自然言語やLLMのテキスト検索にはサイン距離(<=>)を使用してください。

OpenAIやCohereなどの主要なEmbeddingモデルの出力はコサイン類似度で評価されることを前提に設計されています。

ベクトルインデックスの仕組み(IVFFlat vs HNSW)

データが数百件程度であれば、全レコードの距離を愚直に計算(フルスキャン / フラット検索)しても一瞬で終わります。

しかし、データが数万件、数百万件になると、毎回全件と距離を計算していては検索に数秒〜数十秒かかってしまいます。

そこで使われるのが「近似最近傍探索(ANN:Approximate Nearest Neighbor)」インデックスです。完璧な100点満点の最近傍を探す代わりに、「99%の確率で正解に近い上位件数」を圧倒的な超高速で探し出します。

pgvectorは、主に2種類のインデックスアルゴリズムを提供しています。

【IVFFlat】                      【HNSW】
  クラスタリングしてエリアを絞る    多層ネットワークを飛び移りながら探す

     ●   ●   ●   |   ★   ★           [Layer 2]  ○ ─────────→ ○
   ●   (芯)  ●   | ★  (芯)  ★                      │           │
     ●   ●   ●   |   ★   ★           [Layer 1]  ○ ───→ ○ ───→ ○
  ───────────────┼──────────────                    │     │     │
     ▲   ▲   ▲   |   ■   ■           [Layer 0]  ●─●─●─●─●─●─●─●
   ▲   (芯)  ▲   | ■   (芯)  ■           (下層ほど高密度なメッシュ)

① IVFFlat(Inverted File Flat)

  • 仕組み:空間をクラスタ(小部屋)に分割しておき、検索時はクエリに近い代表的な小部屋の中だけを探す。
  • メリット:インデックスの構築が高速で、メモリ消費量が少ない。
  • デメリット:検索精度(Recall)がやや低め。また、**「ある程度データが投入された後でないと適切なインデックスを作れない」**という運用上の罠がある。

② HNSW(Hierarchical Navigable Small World)★実務での圧倒的本命

  • 仕組み:スキップリストのように、粗い階層から細かい階層へと多層ネットワークを経由して一気に目的のベクトルへと近づく。
  • メリット:**検索速度が桁違いに速く、精度(Recall)も極めて高い。**データが空の状態でインデックスを作成し、後からINSERTしていっても性能が落ちにくい。
  • デメリット:インデックス構築にCPUパワーとメモリ(RAM)を多く消費する。

推奨:
現在のpgvector運用のベストプラクティスは、**「特別な制約がない限りHNSWインデックスを採用すること」**です。pgvector 0.5.0以降でHNSWが導入されたことにより、専用ベクトルDBに匹敵する検索速度を獲得しました。


ステップ・バイ・ステップで学ぶpgvectorの基本操作

それでは、実際にpgvectorを動かしてみましょう。

初心者でも自分のPCで数分で実験できるよう、Dockerを用いた手順で解説します。

環境構築(Dockerで一発起動)

ターミナルを開き、公式のpgvector入りDockerイメージを起動します。

docker run --name pgvector-demo \
  -e POSTGRES_PASSWORD=mypassword \
  -p 5432:5432 \
  -d pgvector/pgvector:pg16

起動したら、コンテナ内のPostgreSQL(psql)に接続します。

docker exec -it pgvector-demo psql -U postgres

拡張機能の有効化

PostgreSQLにログインできたら、まずはデータベース内でpgvectorを有効化するコマンドを実行します。

CREATE EXTENSION IF NOT EXISTS vector;

これだけで、PostgreSQLがベクトル対応データベースへと進化しました。

テーブル作成とデータ挿入

今回はわかりやすく、3次元のベクトルを持つ「アイテム(商品)」テーブルを作成してみます。

-- テーブルの作成
CREATE TABLE items (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    category VARCHAR(50),
    embedding vector(3) -- 3次元のベクトル型を定義
);

次に、サンプルデータをいくつか挿入(INSERT)します。
ここでは各軸を [甘さ, 酸っぱさ, 赤さ] と仮定してみましょう。

INSERT INTO items (name, category, embedding) VALUES
('完熟リンゴ',   'フルーツ', '[0.8, 0.4, 0.9]'),
('もぎたてイチゴ', 'フルーツ', '[0.7, 0.6, 0.8]'),
('フレッシュレモン', 'フルーツ', '[0.1, 0.9, 0.1]'),
('宇治抹茶ラテ',   'ドリンク', '[0.6, 0.0, 0.1]'),
('ブラックコーヒー', 'ドリンク', '[0.0, 0.2, 0.0]');

類似度検索クエリの実行

それでは、「甘くて赤いフルーツ」を探すため、クエリベクトル [0.75, 0.5, 0.85] を使ってコサイン距離(<=>)で類似度検索をしてみましょう。

SELECT 
    id, 
    name, 
    category,
    embedding <=> '[0.75, 0.5, 0.85]' AS distance
FROM items
ORDER BY distance ASC
LIMIT 3;

【実行結果のイメージ】

 id |     name      | category |       distance       
----+---------------+----------+----------------------
  1 | 完熟リンゴ    | フルーツ | 0.001648057793444408
  2 | もぎたてイチゴ| フルーツ | 0.007604312781488092
  4 | 宇治抹茶ラテ  | ドリンク |  0.42854964264667316
(3 rows)

コサイン距離が 0 に近いほど「意味が似ている」ことを示します。見事に「完熟リンゴ」と「もぎたてイチゴ」が上位にランクインしました!

HNSWインデックスの作成

データが数百万件に増えた場合に備えて、HNSWインデックスを作成してみましょう。

-- コサイン距離用のHNSWインデックスを作成
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
  • vector_cosine_ops:コサイン距離(<=>)用の演算子クラスを指定します。
    • ユークリッド距離を使う場合は vector_l2_ops
    • 内積を使う場合は vector_ip_ops
  • m = 16:グラフの各ノードが持つ双方向リンクの最大数(デフォルト16)。
  • ef_construction = 64:インデックス構築時の探索範囲(デフォルト64)。大きくするとインデックス作成時間は伸びますが検索精度が向上します。

インデックス作成後に再度検索クエリを発行すると、PostgreSQLのオプティマイザが自動的にHNSWインデックスを使って高速なANN検索を行ってくれます。

実践編:Python × OpenAI × pgvectorでミニRAGシステムを作る

基礎が理解できたところで、実務で最もよく使われる構成である「Python」と「OpenAI API」を組み合わせた実践的なコードを見てみましょう。

全体アーキテクチャ

[日本語テキスト] ──(OpenAI Embedding API)──→ [1,536次元ベクトル] ──→ [PostgreSQL (pgvector)]
                                                                               │
[ユーザーの質問] ──(OpenAI Embedding API)──→ [1,536次元ベクトル] ──→ [類似度検索 (SQL)]

準備

必要なライブラリをインストールします。

pip install openai psycopg2-binary pgvector

実装コード(保存と検索)

以下のPythonスクリプトを実行すると、文章をベクトル化してDBに保存し、質問に関連する社内FAQを検索します。

import os
import psycopg2
from pgvector.psycopg2 import register_vector
from openai import OpenAI

# OpenAIクライアントの初期化(環境変数 OPENAI_API_KEY が必要です)
client = OpenAI()

# 1. データベース接続
conn = psycopg2.connect(
    dbname="postgres",
    user="postgres",
    password="mypassword",
    host="localhost",
    port=5432
)
register_vector(conn) # pgvectorをpsycopg2で扱えるように登録

with conn.cursor() as cur:
    # 拡張機能とテーブルの作成
    cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
    cur.execute("""
        CREATE TABLE IF NOT EXISTS faq_documents (
            id SERIAL PRIMARY KEY,
            question TEXT,
            answer TEXT,
            embedding vector(1536) -- OpenAIのtext-embedding-3-smallは1536次元
        );
    """)
    conn.commit()

# テキストをベクトル化するヘルパー関数
def get_embedding(text: str) -> list[float]:
    response = client.embeddings.create(
        input=text,
        model="text-embedding-3-small"
    )
    return response.data[0].embedding

# 2. サンプル社内FAQデータの挿入
faq_data = [
    {
        "q": "有給休暇の申請方法を教えてください",
        "a": "社内ポータルの『勤怠管理システム』から前日18時までに申請してください。"
    },
    {
        "q": "リモートワーク時の交通費はどうなりますか?",
        "a": "出社日数に応じて実費が月末に精算されて支給されます。"
    },
    {
        "q": "PCを紛失した場合はどこに連絡すべきですか?",
        "a": "直ちに情報システム部セキュリティ担当(内線999)へ電話連絡してください。"
    }
]

print("FAQデータをベクトル化して保存中...")
with conn.cursor() as cur:
    for item in faq_data:
        # 質問文をベクトル化
        vec = get_embedding(item["q"])
        cur.execute(
            "INSERT INTO faq_documents (question, answer, embedding) VALUES (%s, %s, %s);",
            (item["q"], item["a"], vec)
        )
    conn.commit()
print("保存完了!")

# 3. ユーザーの質問で検索する
user_query = "パソコンを電車に置き忘れてしまった!どうすればいい?"
print(f"\nユーザーの質問: 『{user_query}』")

# 質問文をベクトル化
query_vector = get_embedding(user_query)

# 類似度検索を実行
with conn.cursor() as cur:
    cur.execute("""
        SELECT question, answer, embedding <=> %s AS distance
        FROM faq_documents
        ORDER BY distance ASC
        LIMIT 1;
    """, (query_vector,))
    
    row = cur.fetchone()
    print("\n【最も関連度の高いFAQ】")
    print(f"関連質問: {row[0]}")
    print(f"回答内容: {row[1]}")
    print(f"コサイン距離: {row[2]:.4f}")

conn.close()

実行結果の確認

ユーザーは**「パソコンを電車に置き忘れてしまった」**と質問しており、「PC」や「紛失」という単語は一文字も入っていません。

それにもかかわらず、埋め込みベクトルのおかげで、

  • 「PCを紛失した場合はどこに連絡すべきですか?」

というFAQが最高の類似度(最小のコサイン距離)でピンポイントにヒットします。これがベクトル検索の威力です。


一歩先を行く!ハイブリッド検索(全文検索+ベクトル検索)

pgvectorを語る上で欠かせないのが、PostgreSQLが得意とする「ハイブリッド検索(Hybrid Search)」です。

なぜベクトル検索だけでは不十分なのか?

ベクトル検索は万能に見えますが、明確な弱点があります。

  • 型番や固有名詞の検索:
    • 「型番 XR-5000 のマニュアル」を探したいとき、ベクトル検索は「XR-5001」や「XR-4000」などの似たような製品文字列を「意味が近い」と判定してしまい、正確に区別できないことがあります。
  • 特定のエラーコード:
    • 「エラーコード 0x80070005」をそのまま当てたいのに、一般的な「アクセス拒否エラー」の文章を優先してしまうことがあります。

このため、実務の高品質な検索エンジンでは、「単語の完全一致に強い全文検索(BM25やpg_trgm)」と「文脈に強いベクトル検索」を合体させたハイブリッド検索が業界標準となっています。

PostgreSQLならではのハイブリッド実装

PostgreSQLなら、別の検索サーバー(Elasticsearch等)を用意することなく、同一のDB内でこのハイブリッド検索を完結できます。

代表的な手法が、RRF(Reciprocal Rank Fusion:相互順位融合法)と呼ばれるアルゴリズムです。

[クエリ] ──┬──→ [全文検索 (キーワード一致)] ──→ 順位リストA ──┐
          │                                                  ├──→ [RRFスコアで統合] ──→ 最終結果
          └──→ [ベクトル検索 (意味の近さ)]   ──→ 順位リストB ──┘

SQLでのRRFハイブリッド検索イメージ

WITH semantic_search AS (
    -- 1. ベクトル検索(上位20件)
    SELECT id, RANK() OVER (ORDER BY embedding <=> '[...クエリベクトル...]') as rank
    FROM documents
    ORDER BY embedding <=> '[...クエリベクトル...]'
    LIMIT 20
),
keyword_search AS (
    -- 2. 全文検索(上位20件)
    SELECT id, RANK() OVER (ORDER BY ts_rank_cd(to_tsvector('japanese', content), query) DESC) as rank
    FROM documents, plainto_tsquery('japanese', '検索キーワード') query
    WHERE to_tsvector('japanese', content) @@ query
    LIMIT 20
)
-- 3. 二つの順位をRRFスコア(1 / (60 + rank))で足し合わせる
SELECT 
    COALESCE(s.id, k.id) AS id,
    COALESCE(1.0 / (60 + s.rank), 0.0) +
    COALESCE(1.0 / (60 + k.rank), 0.0) AS rrf_score
FROM semantic_search s
FULL OUTER JOIN keyword_search k ON s.id = k.id
ORDER BY rrf_score DESC
LIMIT 5;

このように、両方の長所を組み合わせることで、「型番や固有名詞も正確に拾いながら、関連する概念も逃さない」究極の検索システムを構築できます。

パフォーマンスを引き出す運用の勘所

pgvectorを数十万〜数千万件の本番環境でトラブルなく動かすためには、PostgreSQLのメモリ構造とインデックスの特性を理解しておく必要があります。

メモリ設定の最適化

ベクトルデータは通常の数値や文字列に比べて圧倒的にデータサイズが大きいです。

たとえば、1,536次元のfloat4ベクトルは1件あたり約6KB(1536 × 4バイト)を消費します。100万件あれば、ベクトルデータだけで約6GBになります。

インデックス作成や検索を高速に行うには、以下のPostgreSQLパラメータを適切に調整してください。

① maintenance_work_mem

HNSWやIVFFlatのインデックスを作成する際に使用されるメモリ量です。デフォルト(64MB等)のままだとインデックス構築がディスクI/Oだらけになり、途方もない時間がかかります。

-- セッション単位またはpostgresql.confで数GB単位に増やす(環境のRAMに応じて)
SET maintenance_work_mem = '4GB';

② shared_buffers

HNSWインデックス全体がメモリ(キャッシュ)にすっぽり収まるのが理想です。本番環境のサーバーRAMの25%〜40%程度を shared_buffers に割り当て、インデックスがディスクからではなくメモリから即座に読み出せるようにサイジングします。

8-2. HNSWパラメータのトレードオフ

HNSWインデックスを作成する際は、2つのパラメータで「構築時間・インデックスサイズ」と「検索速度・精度」のバランスを取ります。

  • m(デフォルト 16):1ノードあたりのリンク数(推奨範囲: 16〜64)。
    • 値を大きくすると:検索精度が上がり、より複雑な空間でも正確に探せる。
    • デメリット:メモリ消費が増え、インデックス作成時間が長くなる。
  • ef_construction(デフォルト 64):インデックス構築時の探索深さ(推奨範囲: 64〜256)。
    • 値を大きくすると:構築精度が高くなる。
    • デメリット:構築時間が大幅に増加する(検索時のメモリ消費には影響しない)。

また、検索時には以下のセッション変数を変更することで、一時的に検索クオリティを上げることができます。

-- 検索時の探索深さ(デフォルト 40)。大きくすると検索が遅くなる代わりに精度が向上する
SET hnsw.ef_search = 100;

主要クラウドでのサポート状況

「pgvectorって、自分でLinuxサーバーを立ててビルドしなきゃいけないの?」と心配するかもしれませんが、現在は主要なクラウドDBのほぼすべてで標準サポートされています。

  • AWS:Amazon RDS for PostgreSQL、Amazon Aurora PostgreSQLで数年前から利用可能。
  • Google Cloud:Cloud SQL for PostgreSQL、AlloyDBでサポート。
  • Microsoft Azure:Azure Database for PostgreSQLでサポート。
  • Supabase:標準でpgvectorが組み込まれており、管理画面のトグルスイッチひとつで有効化可能。

そのため、現在すでにクラウド上のPostgreSQLを使っているプロジェクトであれば、追加インフラを契約することなく、今すぐ本番環境でpgvectorを使い始めることができます。

よくある質問(FAQ)

Q1. 何件くらいのデータ量までpgvectorで耐えられますか?

A. 数百万件〜数千万件の規模であれば、適切なサーバー選定(RAM容量)とHNSWインデックスにより、数ミリ秒〜数十ミリ秒で検索可能です。

1億件を超えるような超巨大規模になると、PostgreSQL単体でのメモリ保持や分散の難易度が上がってくるため、専用の分散ベクトルDB(MilvusやPineconeなど)を検討する価値が出てきます。しかし、世の中のWebサービスの95%以上は1000万件以下に収まるため、ほとんどのユースケースでpgvectorが最適解となります。

Q2. ベクトルの正規化(Normalize)は必要ですか?

A. コサイン距離(<=>)を使う場合は、事前に正規化しなくても正しい結果が得られます。

ただし、アプリケーション側で事前にベクトルの長さを1に正規化(L2 Norm = 1)して保存し、内積演算子(<#>)を使って検索すると、余計な平方根計算を省略できるため、検索スループットをさらに高めることができます。

Q3. 更新(UPDATEやDELETE)が頻繁にあるテーブルでも大丈夫ですか?

A. HNSWインデックスは更新や削除にも対応しています。

ただし、PostgreSQLの仕様上、頻繁な更新・削除はデッドタプルを発生させます。定期的な VACUUM を適切に実行し、テーブルやインデックスの肥大化(Bloat)を防ぐ運用は通常のPostgreSQLと同様に重要です。


まとめ:まずは既存のPostgreSQLに CREATE EXTENSION から始めよう

AIとデータベースの境界線は、いま急速に溶け合っています。

かつては「AIアプリケーションを作るなら専用のベクトルDBを導入しなければならない」と考えられていましたが、pgvectorの進化(特にHNSW対応やhalfvec対応)によって、その常識は完全に覆されました。

本記事の重要ポイントのおさらい

  1. pgvectorとは:PostgreSQLにベクトル保存・検索機能を追加する大人気オープンソース拡張機能。
  2. 選ばれる理由:専用DBを増やさずに、従来のRDBMSデータとベクトルデータを単一のPostgreSQLでACIDトランザクションを保ちながら管理・JOINできる。
  3. インデックスの選択:実務・本番ではHNSW(階層型ナビゲーブルスモールワールド)が圧倒的本命。
  4. 指標の選択:テキスト検索・RAGには**コサイン距離(<=>)**が基本。
  5. 強力なハイブリッド:PostgreSQLの全文検索とベクトル検索を組み合わせることで、完全一致と意味検索の「いいとこ取り」が可能。

もしあなたがすでにPostgreSQLを使っているなら、新しいデータベース製品を調査・調達し、複雑なインフラパイプラインを組む必要はありません。

まずは手元のPostgreSQLで、この魔法の呪文を唱えてみてください。

CREATE EXTENSION vector;

あなたの使い慣れたリレーショナルデータベースが、AI時代の最先端セマンティック検索エンジンへと進化する旅は、そこから始まります。

タイトルとURLをコピーしました