Home 論文解説: pgvectorscale — PostgreSQLをAIアプリケーション向けにスケールさせる拡張
投稿
キャンセル

📄 論文解説: pgvectorscale — PostgreSQLをAIアプリケーション向けにスケールさせる拡張

論文概要(Abstract)

pgvectorscaleは、TimescaleDBチームが開発したPostgreSQL拡張で、pgvectorのベクトル検索性能を大幅に向上させます。2つの核心技術 — StreamingDiskANN(DiskANNアルゴリズムのPostgreSQL統合版)とSBQ(Statistical Binary Quantization)— を導入し、標準pgvectorのHNSWインデックスと比較して最大28倍のQPS向上を実現しています。PostgreSQLエコシステムを離れることなく、専用ベクトルDBに匹敵する検索性能を得られる点が最大の魅力です。

この記事は Zenn記事: 2026年版ベクトルDB選定ガイド:pgvector・Qdrant・Pineconeを本番ベンチマークで比較 の深掘りです。

情報源

背景と動機(Background & Motivation)

pgvectorはPostgreSQL上でベクトル検索を実現する拡張として広く普及していますが、大規模データセット(100万件以上)では性能面で専用ベクトルDB(Qdrant、Milvus等)に後れを取る場面が出てきます。特にHNSWインデックスのメモリ消費量と高次元ベクトル(768〜1536次元)でのQPS低下が課題でした。

一方で、PostgreSQLを使い続けたい理由は強力です。JOIN・WHERE句によるリレーショナルクエリとの統合、ACID保証、既存の監視・バックアップ基盤の流用、運用チームの学習コスト最小化など、組織的な利点が大きいのです。

pgvectorscaleは「PostgreSQLから離れずに専用DB級の性能を」というニーズに応える拡張です。

主要な貢献(Key Contributions)

  • StreamingDiskANN: MicrosoftのDiskANNアルゴリズムをPostgreSQL内部に統合。ディスクベースのグラフインデックスにより、メモリ使用量を1/4に削減しながら高いRecallを維持
  • SBQ(Statistical Binary Quantization): ベクトルの統計的性質を利用した二値量子化。各次元を1ビットに圧縮し、メモリ内での高速フィルタリングに使用。最終スコアはfloat32で再計算
  • pgvector互換API: 既存の <-> (L2)、<=> (cosine)、<#> (内積) 演算子をそのまま使用可能。アプリケーションコードの変更が最小限

技術的詳細(Technical Details)

StreamingDiskANN アーキテクチャ

StreamingDiskANNは、DiskANNのVamanaグラフ構造をPostgreSQLのストレージエンジン上に構築します。

Vamanaグラフの構築:

グラフ $G = (V, E)$ において、各ノード $v \in V$ はデータベース内の1行(ベクトル)に対応します。エッジは近傍関係を表し、以下の2つの性質を満たすように構築されます:

\[\forall u, v \in V: d(u, v) \leq R \cdot d(u, \text{medoid})\] \[\text{degree}(v) \leq R_{\max} \quad \forall v \in V\]

ここで、

  • $d(u, v)$: ノード $u$ と $v$ の距離(コサイン距離またはL2距離)
  • $R$: グラフの「到達可能性」パラメータ
  • $R_{\max}$: ノードの最大次数(デフォルト50)
  • medoid: データセットの中心点

ディスクレイアウト最適化:

PostgreSQLのページ(8KB)にベクトルデータとグラフエッジを共に格納します。検索時のSSD読み取りを最小化するため、近傍ノードを同一ページまたは隣接ページに配置するレイアウト最適化を行います。

ストリーミング更新:

従来のDiskANNはバッチ構築が前提でしたが、StreamingDiskANNはINSERT/UPDATE/DELETEに対応するオンライン更新をサポートします。新しいベクトルの挿入時、既存グラフに対してGreedy Search → エッジ追加 → プルーニングの3ステップで増分的にインデックスを更新します。

SBQ(Statistical Binary Quantization)

SBQは、各次元の値をその統計的分布に基づいて二値化します。

量子化プロセス:

ベクトル $\mathbf{x} \in \mathbb{R}^D$ に対して、各次元 $i$ の二値化は以下の通りです:

\[b_i = \begin{cases} 1 & \text{if } x_i \geq \mu_i + \alpha \cdot \sigma_i \\ 0 & \text{otherwise} \end{cases}\]
  • $\mu_i$: 次元 $i$ の平均値(データセット全体から計算)
  • $\sigma_i$: 次元 $i$ の標準偏差
  • $\alpha$: 閾値パラメータ(デフォルト0、つまり平均値で分割)

ハミング距離による高速フィルタリング:

二値化されたベクトル間のハミング距離は、CPUのPOPCNT命令で高速に計算できます:

\[d_H(\mathbf{b}_q, \mathbf{b}_x) = \text{popcount}(\mathbf{b}_q \oplus \mathbf{b}_x)\]

768次元ベクトルの場合、float32表現(3072バイト)が96バイトのビット列に圧縮され、32倍の圧縮率を達成します。

2段階検索:

  1. SBQによるハミング距離で上位候補をフィルタリング(高速・近似的)
  2. 候補に対してfloat32での正確な距離計算(高精度・低速)

この2段階アプローチにより、メモリ使用量を大幅に削減しながら高いRecallを維持します。

パラメータ設定ガイド

パラメータデフォルト値推奨範囲影響
num_neighbors5030-100グラフ次数。大きいほどRecall↑、メモリ↑
search_list_size10050-200検索時の探索幅。大きいほどRecall↑、レイテンシ↑
max_alpha1.21.0-2.0グラフ構築時のプルーニング強度
num_bits_per_dimension11-2SBQ量子化ビット数。2で精度↑、メモリ↑

実装のポイント(Implementation)

インストールと基本設定

1
2
3
4
# pgvectorscale のインストール(Rust/pgrx ビルドチェーンが必要)
git clone https://github.com/timescale/pgvectorscale.git
cd pgvectorscale/pgvectorscale
cargo pgrx install --release
1
2
3
-- PostgreSQL内での有効化(pgvector が先に必要)
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;

DiskANNインデックスの作成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- テーブル作成
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding vector(768) NOT NULL,
    category TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- DiskANNインデックス作成(pgvectorscale独自)
CREATE INDEX idx_docs_diskann
ON documents USING diskann (embedding);

-- カスタムパラメータでのインデックス作成
CREATE INDEX idx_docs_diskann_tuned
ON documents USING diskann (embedding)
WITH (num_neighbors = 64, search_list_size = 128);

検索クエリ(pgvector互換)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import psycopg
from pgvector.psycopg import register_vector

def search_documents(
    query_embedding: list[float],
    category: str | None = None,
    limit: int = 10,
) -> list[dict]:
    """pgvectorscale DiskANNインデックスを使用したベクトル検索

    Args:
        query_embedding: クエリベクトル(768次元)
        category: フィルタ条件(オプション)
        limit: 返却件数

    Returns:
        検索結果のリスト
    """
    conn = psycopg.connect("postgresql://user:pass@localhost/mydb")
    register_vector(conn)

    # search_list_size をセッション単位で調整
    conn.execute("SET diskann.query_search_list_size = 128")

    if category:
        results = conn.execute(
            """
            SELECT id, content,
                   1 - (embedding <=> %s::vector) AS similarity
            FROM documents
            WHERE category = %s
            ORDER BY embedding <=> %s::vector
            LIMIT %s
            """,
            [query_embedding, category, query_embedding, limit],
        ).fetchall()
    else:
        results = conn.execute(
            """
            SELECT id, content,
                   1 - (embedding <=> %s::vector) AS similarity
            FROM documents
            ORDER BY embedding <=> %s::vector
            LIMIT %s
            """,
            [query_embedding, query_embedding, limit],
        ).fetchall()

    return [
        {"id": r[0], "content": r[1], "similarity": r[2]}
        for r in results
    ]

pgvector HNSWからの移行

1
2
3
4
5
6
7
8
9
-- 既存のHNSWインデックスを削除
DROP INDEX IF EXISTS idx_docs_hnsw;

-- DiskANNインデックスに置換
CREATE INDEX idx_docs_diskann
ON documents USING diskann (embedding);

-- アプリケーションコードの変更は不要!
-- <=> (cosine), <-> (L2), <#> (inner product) がそのまま使える

Production Deployment Guide

AWS実装パターン(コスト最適化重視)

トラフィック量別の推奨構成:

規模月間リクエスト推奨構成月額コスト主要サービス
Small~3,000 (100/日)Serverless$80-200Lambda + EC2 PostgreSQL + pgvectorscale
Medium~30,000 (1,000/日)Hybrid$400-900ECS Fargate + EC2 PostgreSQL + ElastiCache
Large300,000+ (10,000/日)Container$2,500-6,000EKS + EC2 PostgreSQL Cluster + ElastiCache

重要: pgvectorscaleはRDSでは利用不可(pgrx/Rustビルドチェーンが必要)。EC2またはEKS上のself-hosted PostgreSQLが前提です。

Small構成の詳細(月額$80-200):

  • EC2 r6g.medium (PostgreSQL 16 + pgvectorscale): 1 vCPU, 8GB RAM ($50/月)
  • Lambda: 検索API ($20/月)
  • EBS gp3 100GB: データ格納 ($8/月)
  • CloudWatch: 監視 ($5/月)

Medium構成の詳細(月額$400-900):

  • EC2 r6g.xlarge (PostgreSQL 16 + pgvectorscale): 4 vCPU, 32GB RAM ($200/月)
  • EC2 r6g.large (レプリカ): 読み取り分散 ($100/月)
  • ECS Fargate: API 0.5 vCPU × 2タスク ($120/月)
  • ElastiCache Redis (cache.t3.micro): クエリキャッシュ ($15/月)
  • EBS gp3 500GB: ($40/月)

コスト削減テクニック:

  • DiskANNインデックスによりメモリ使用量を1/4に削減(r6g.xlargeで十分に)
  • SBQ量子化でインメモリフィルタリングを高速化
  • ElastiCacheで頻出クエリをキャッシュ(DB負荷70%削減)
  • EC2 Reserved Instances (1年)で40%割引

コスト試算の注意事項:

  • 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です
  • pgvectorscaleはself-hosted PostgreSQLが必須のため、RDS料金は適用外です
  • 最新料金は AWS料金計算ツール で確認してください

Terraformインフラコード

Small構成: EC2 PostgreSQL + pgvectorscale

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# --- VPC基盤 ---
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"

  name = "pgvectorscale-vpc"
  cidr = "10.0.0.0/16"
  azs  = ["ap-northeast-1a", "ap-northeast-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = true  # コスト削減
}

# --- EC2 (PostgreSQL + pgvectorscale) ---
resource "aws_instance" "postgres" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "r6g.medium"
  subnet_id     = module.vpc.private_subnets[0]

  root_block_device {
    volume_type = "gp3"
    volume_size = 100
    iops        = 3000
    throughput  = 125
    encrypted   = true
  }

  user_data = <<-EOF
    #!/bin/bash
    apt-get update && apt-get install -y postgresql-16 postgresql-16-pgvector
    # pgvectorscale requires Rust toolchain
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    source $HOME/.cargo/env
    cargo install cargo-pgrx --version 0.12.0
    git clone https://github.com/timescale/pgvectorscale.git
    cd pgvectorscale/pgvectorscale && cargo pgrx install --release
    systemctl restart postgresql
  EOF

  tags = { Name = "pgvectorscale-db" }
}

# --- セキュリティグループ ---
resource "aws_security_group" "postgres" {
  vpc_id = module.vpc.vpc_id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.lambda.id]
  }
}

# --- CloudWatch アラーム ---
resource "aws_cloudwatch_metric_alarm" "disk_usage" {
  alarm_name          = "pgvectorscale-disk-usage"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name         = "DiskSpaceUtilization"
  namespace           = "CWAgent"
  period              = 300
  statistic           = "Average"
  threshold           = 80
  alarm_description   = "ディスク使用率80%超過(DiskANNインデックス拡大)"
}

運用・監視設定

1
2
3
4
5
6
7
-- CloudWatch Logs Insights: DiskANNインデックス性能モニタリング
-- pgvectorscale固有のメトリクス
SELECT
    schemaname, indexname, idx_scan, idx_tup_read, idx_tup_fetch,
    pg_size_pretty(pg_relation_size(indexrelid)) as index_size
FROM pg_stat_user_indexes
WHERE indexname LIKE '%diskann%';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import boto3

cloudwatch = boto3.client('cloudwatch')

# DiskANNインデックスサイズ監視
cloudwatch.put_metric_alarm(
    AlarmName='pgvectorscale-index-size',
    ComparisonOperator='GreaterThanThreshold',
    EvaluationPeriods=1,
    MetricName='DiskSpaceUtilization',
    Namespace='CWAgent',
    Period=3600,
    Statistic='Maximum',
    Threshold=80,
    AlarmDescription='DiskANNインデックスのディスク使用量80%超過'
)

コスト最適化チェックリスト

  • ~100 req/日 → EC2 r6g.medium + pgvectorscale ($80-200/月)
  • ~1,000 req/日 → EC2 r6g.xlarge + レプリカ ($400-900/月)
  • 10,000+ req/日 → EKS + PostgreSQL HA Cluster ($2,500-6,000/月)
  • DiskANNインデックスでメモリ使用量を1/4に削減
  • SBQ量子化でフィルタリング性能を向上
  • ElastiCacheでホットクエリキャッシュ(70%のDB負荷削減)
  • EC2 Reserved Instances (1年コミット)で40%割引
  • EBS gp3のIOPS/スループットを実際の負荷に合わせて調整
  • pgvectorscaleのnum_neighborsを用途に応じて最適化(30-100)
  • search_list_sizeをRecall要件に応じて調整(50-200)
  • VACUUM ANALYZEの定期実行でインデックス性能を維持
  • pg_stat_user_indexesでインデックス使用状況を定期監視
  • 不要になったHNSWインデックスを削除(DiskANNへの完全移行後)
  • WALアーカイブのライフサイクルポリシー設定(S3 Glacier移行)
  • CloudWatch Anomaly Detectionでクエリレイテンシ異常検知
  • タグ戦略: env/project/teamでコスト可視化
  • 開発環境はt3.medium(最小構成)に縮小
  • pg_cron で自動VACUUM/ANALYZE をスケジュール設定
  • 接続プーリング(PgBouncer)で接続数を最適化
  • EBS Snapshot のライフサイクルポリシー設定(7日保持)

実験結果(Results)

pgvector vs pgvectorscale ベンチマーク

Cohere Wikipedia Embeddings (100万件、768次元):

インデックスQPS (Recall@10=0.90)メモリ使用量インデックスサイズ
pgvector HNSW (M=16)854.2 GB3.8 GB
pgvectorscale DiskANN4701.1 GB2.1 GB
改善率5.5倍73%削減45%削減

OpenAI text-embedding-3-small (100万件、1536次元):

インデックスQPS (Recall@10=0.90)メモリ使用量インデックスサイズ
pgvector HNSW (M=16)328.5 GB7.6 GB
pgvectorscale DiskANN9002.1 GB4.2 GB
改善率28倍75%削減45%削減

高次元(1536次元)でのQPS改善率が特に大きく、LLMの埋め込みモデル(OpenAI text-embedding-3, Cohere embed等)との組み合わせで威力を発揮します。

実運用への応用(Practical Applications)

Zenn記事で紹介した「pgvectorscaleがQdrantの11.4倍のスループット」というTiger Dataのベンチマーク結果は、本論文のStreamingDiskANN技術によるものです。

PostgreSQL利用中の組織への推奨移行パス:

  1. pgvectorで開始(CREATE EXTENSION vector)
  2. 100万件超でQPS不足が発生したら、pgvectorscaleを追加
  3. HNSWインデックスをDiskANNインデックスに置換(アプリコード変更なし)
  4. SBQパラメータを調整してメモリ/Recallのバランスを最適化

注意点: pgvectorscaleはself-hosted PostgreSQLが前提です。AWS RDS/Auroraでは現時点で利用できません。EC2またはEKS上でPostgreSQLを運用する必要があります。

関連研究(Related Work)

  • DiskANN (Subramanya et al., 2019; Jayaram et al., 2023): pgvectorscaleの基盤となるANNアルゴリズム。SSDベースのグラフインデックスで10億件規模に対応
  • ScaNN (Guo et al., 2020): Googleの量子化ベースANNライブラリ。SBQの設計にインスピレーションを与えた
  • pgvector (Katz, 2021-): PostgreSQLのベクトル検索拡張。pgvectorscaleの依存先であり、演算子互換性を維持

まとめと今後の展望

pgvectorscaleは、PostgreSQLユーザーにとっての「最適解の1つ」です。DiskANNの効率的なディスクベースインデックスとSBQの量子化技術により、専用ベクトルDBに匹敵する性能をPostgreSQL内で実現します。

今後の課題として、RDS/Aurora対応(AWSマネージドサービスでの利用)、マルチテナント対応の改善、および10億件規模でのベンチマーク検証が挙げられます。

参考文献

この投稿は CC BY 4.0 でライセンスされています。

ICLR 2025論文解説: RouteLLM — 選好データで学習するLLMルーティングフレームワーク

論文解説: Evaluation-Driven Development and Operations(EDD)— LLMエージェントのライフサイクル評価統合パラダイム