データベース

PGliteとは?Docker不要のPostgreSQLの使い方とpgvectorでの類似度検索【0.5系】

PGliteとは?Docker不要のPostgreSQLの使い方とpgvectorでの類似度検索【0.5系】

PGlite(ピージーライト)は、PostgreSQLをWebAssembly(WASM)にビルドし、TypeScriptのライブラリとして配布しているデータベースです。npm install だけでNode.js・Bun・Deno・ブラウザの中にPostgreSQLが立ち上がるため、ローカル開発やテストのためにDockerでPostgreSQLのコンテナを起動する必要がなくなります。

この記事は、2026年10月28日公開の @electric-sql/pglite 0.5.13 を実際に動かした結果をもとに、導入、Dockerの代わりに使う手順、Pythonからの利用、pgvectorでの類似度検索までをまとめたものです。0.5.0で拡張機能の読み込み方が変わったため、ネット上の多くのサンプルはそのままでは動きません。その点も実行結果つきで説明します。

まとめ:PGliteの要点

  • PGliteはWASM版のPostgreSQLです。0.5.13の SELECT version() は「PostgreSQL 18.6 (PGlite 0.5.13)」を返します。
  • データはメモリ・ファイル(Node.js)・IndexedDB(ブラウザ)のいずれかに置けます。接続は1本だけなので、本番サーバーのPostgreSQLの代わりにはなりません。
  • Dockerの代わりに使うときは pglite-server を起動します。psqlやORMから postgresql://postgres:[email protected]:5432/postgres で接続できますが、複数の接続は1本ずつ順番に処理されます。
  • pgvectorは0.5.0から別パッケージの @electric-sql/pglite-pgvector です。旧来の @electric-sql/pglite/vector は ERR_PACKAGE_PATH_NOT_EXPORTED で失敗します。
  • 文章の類似度検索はコサイン距離 <=> を使います。Intel MacのNode.jsで384次元・1万件を測ると、インデックスなしの検索が21〜71ミリ秒、HNSWの構築が約45秒(同じPCのネイティブのPostgreSQL 18.6では約2秒)でした。インデックスは、実データで測って応答時間が足りなくなってから張ります。

PGliteの仕組みと現在の版

PGliteはElectricSQLが開発しているオープンソースのプロジェクトで、Apache-2.0とPostgreSQL Licenseのデュアルライセンスから選択できます。公式READMEは「Node.js、Bun、Denoとブラウザで動き、gzipで3MB、pgvectorやPostGISなど多くの拡張に対応する」と説明しています。PostgreSQL本体のシングルユーザーモードをWASMから呼び出す作りで、Linuxの仮想マシンは使いません。ネイティブのPostgreSQLを別にインストールする必要もありません。npmのダウンロード数APIによると、2026年10月22〜28日の1週間のダウンロード数は36,019,104回でした。

同じREADMEには「PGlite is single user/connection.」という制約も明記されています。PGliteは1つのプロセスの中から1本の接続で使うデータベースで、ネットワーク越しに複数のアプリから共有する使い方は想定していません。

0.4系と0.5系の違い

2026年6月2日公開の0.5.0で、CHANGELOGに「Upgrade to Postgres 18.3; move other extensions to their own npm packages」とあるとおり、PostgreSQLが18系に上がり、pgvectorなどの拡張が本体から別パッケージに分かれました。0.5.9でPostgreSQL 18.6に更新されています。手元で両方の版を動かした結果は次のとおりです。

PGlite PostgreSQL pgvector pgvectorのimport元
0.4.6 17.5 0.8.1 @electric-sql/pglite/vector
0.5.13 18.6 0.8.7 @electric-sql/pglite-pgvector

0.5.13で旧来の書き方のまま import('@electric-sql/pglite/vector') を実行すると、ERR_PACKAGE_PATH_NOT_EXPORTED(Package subpath ‘./vector’ is not defined by “exports”)で止まります。ZennやDevelopersIOで公開されている2024〜2025年の解説記事は、確認した範囲ではすべてこの旧パスで書かれていました。

もう1つの落とし穴はデータディレクトリです。0.4.6で作ったディレクトリ(PG_VERSION は17)を0.5.13で開くと「PGlite failed to initialize properly」で起動しません。PostgreSQLのメジャー版が違うデータファイルは読めないためです。ファイルやIndexedDBにデータを残している場合は、上げる前に旧版で pglite-toolsのpgDump を使ってSQLとして書き出し、新しい版の exec() で流し込みます。

Node.jsでのPGlite導入と永続化設定

本体はnpmパッケージ1つです。Bunなら bun install、Denoなら deno add npm:@electric-sql/pglite で入ります。

npm install @electric-sql/pglite
import { PGlite } from '@electric-sql/pglite'

const db = await PGlite.create()           // メモリ上(プロセス終了で消える)
// const db = await PGlite.create('./pgdata')        Node.jsでファイルに保存
// const db = await PGlite.create('idb://my-pgdata')  ブラウザでIndexedDBに保存

const { rows } = await db.query('SELECT version()')
console.log(rows[0].version)
// PostgreSQL 18.6 (PGlite 0.5.13) on wasm32-unknown-emscripten, ...

Intel MacのNode.js 26.5.0で計測すると、メモリ上のインスタンスの起動には約6〜7秒かかりました。起動のたびに初期化(initdb相当)が走るためです。テストでテストケースごとに新しく作ると、その分だけ遅くなります。テストファイルごとに1回作り、ケースの間は TRUNCATE やトランザクションのロールバックで状態を戻すほうが速く済みます。ファイル保存の ./pgdata は空の状態で約38MBでした。

ORMからはドライバ経由で使えます。Drizzleは drizzle-orm/pglite、Prismaはドライバアダプタを経由して接続します。Drizzle側の書き方はDrizzle ORM(ドリズル)とは?使い方・Prismaとの比較・v1.0 RCの変更点で扱っています。ブラウザの複数タブで1つのデータベースを共有したい場合は、PGliteWorkerで1つのWorkerに集約します。

pglite-serverによるDocker代替と接続制約

アプリが pg や psycopg のような通常のPostgreSQLクライアントで接続する作りの場合、PGliteをimportするようにコードを書き換えなくても済みます。@electric-sql/pglite-socket に入っている pglite-server が、PGliteをPostgreSQLのワイヤプロトコルで公開するからです。docker run postgres の代わりにこれを起動します。

psqlやORMから接続する手順

npm install @electric-sql/pglite @electric-sql/pglite-socket @electric-sql/pglite-pgvector

npx pglite-server --db=./pgdata --port=5432 \
  --extensions=@electric-sql/pglite-pgvector:vector

# 別のターミナルから(SSLには対応していない)
PGSSLMODE=disable psql "postgresql://[email protected]:5432/postgres"

拡張を読み込むときは、--extensions=vector と書かないでください。0.2.16の --help は vector を組み込みの拡張として例示していますが、実行すると存在しないパッケージ @electric-sql/pglite-vector を探して起動に失敗しました。パッケージ名:エクスポート名 の形で書けば読み込まれ、CREATE EXTENSION vector のあと extversion は0.8.7になります。

開発サーバーと一緒に起動したいときは --run を使います。--include-database-url を付けると、子プロセスの環境変数 DATABASE_URL に postgresql://postgres:[email protected]:5432/postgres が入ります。0.2.16で試すと、子プロセスが終了コード2で失敗したときはサーバーも停止しましたが、終了コード0で正常に終わったときはサーバーが起動したまま残りました。テストのスクリプトを --run で流す場合は、終了後にサーバーのプロセスを止める処理を入れておきます。

npx pglite-server --run "npm run dev" --include-database-url

PostgreSQLのワイヤプロトコルを使うため、GoのpgxやPythonのSQLAlchemyからも接続する構成を取れます。ただしSSL、接続プール、セッション状態などの互換性は、使用するドライバと設定で確認します。

pglite-serverの同時接続制限と待機動作

PGlite本体の接続は1本だけなので、pglite-serverは複数の接続を1本にまとめて順番に処理します。既定の --max-connections は1です。1本目のpsqlで SELECT pg_sleep(5) を実行している間に2本目を繋ぐと、次の結果になりました。

設定 2本目の接続
既定(–max-connections=1) server closed the connection unexpectedly
–max-connections=5 約4秒待たされたあと成功

接続プールを持つWebフレームワークやORM、並列実行するテストランナーは複数の接続を張ります。-m を上げないと2本目で接続が切れ、上げても処理は並行せず待たされます。pglite-socketのドキュメントも、通常のPostgreSQLとは違うためすべての使い方が動く保証はない、と注意しています。

Docker・Testcontainersとの使い分け

判断の基準は「本番と同じPostgreSQLの挙動が必要かどうか」です。

  • PGliteで足りる:単体テストやリポジトリ層のテスト、ローカルでの試作、ブラウザだけで完結するデモ。Dockerデーモンが無いCIやPCでもそのまま動きます。
  • DockerやTestcontainersにする:同時に書き込む処理やロック・デッドロックの検証、レプリケーション、PGliteで利用できないPostgreSQL拡張、本番と同じマイナー版で確かめたい移行テスト。コンテナで使い捨てのDBを作る方法はTestcontainersとは?Dockerで作る使い捨てテスト環境と2.0系の移行点で解説しています。

py-pgliteによるPythonテストと依存環境

PythonからPGliteを使うライブラリが py-pglite です。PyPIでの説明は「Python testing library for PGlite」で、用途はpytestで使う一時的なPostgreSQLです。中身はPythonでPostgreSQLを再実装したものではありません。Python 3.14の仮想環境に0.5.3を入れて確かめると、次のように動いていました。

  • 初回の起動時に作業ディレクトリで npm install を実行し、Node.jsでPGliteとpglite-socketを起動してソケットで接続する。Node.jsとnpmが入っていないと使えません。
  • 指定しているPGliteは ^0.3.0 のため、PostgreSQLは17.4、pgvectorは0.8.0でした。0.5系のPGliteとは版が違います。
  • Pythonは3.10以上が必要です(macOS標準の3.9では入りません)。最新の0.5.3は2025年9月17日公開で、それ以降の更新はありません。
pip install "py-pglite[extensions,sqlalchemy,psycopg]"
from sqlalchemy import text
from py_pglite import PGliteConfig
from py_pglite.sqlalchemy import SQLAlchemyPGliteManager

def test_vector():
    cfg = PGliteConfig(extensions=["pgvector"])
    with SQLAlchemyPGliteManager(cfg) as m:
        with m.get_engine().connect() as c:
            c.execute(text("CREATE EXTENSION IF NOT EXISTS vector"))
            d = c.execute(text("SELECT '[1,0]'::vector <=> '[1,1]'::vector")).scalar()
            assert round(d, 4) == 0.2929

このテストは5.18秒で通りました。SQLAlchemyを入れているとpytestのフィクスチャ pglite_engine と pglite_session も使えます。本番のPythonアプリは通常のPostgreSQLとpgvectorに接続し、py-pgliteはそのテスト用、と分けて使うのが前提です。

pgvectorによる類似度検索の実装

ここからはPGliteにpgvectorを入れて、文章の意味の近さで検索する手順です。pgvector自体のインストールや仕様の全体像はpgvectorとは?インストール手順(Windows・Ubuntu・Docker)と使い方にまとめています。

拡張の有効化とvector列の作成

PGliteの拡張一覧のとおり、0.5系では別パッケージから vector をimportして extensions に渡します。

npm install @electric-sql/pglite @electric-sql/pglite-pgvector
import { PGlite } from '@electric-sql/pglite'
import { vector } from '@electric-sql/pglite-pgvector'

const db = await PGlite.create({ extensions: { vector } })
await db.exec(`
  CREATE EXTENSION IF NOT EXISTS vector;
  CREATE TABLE faqs (
    id serial PRIMARY KEY,
    question text,
    category text,
    embedding vector(1536)
  );
`)

extensions を渡さずに CREATE EXTENSION vector を実行すると「extension “vector” is not available」になります。vector(N) の次元と違う長さを入れると「expected 3 dimensions, not 2」のように拒否されるため、Embeddingモデルの出力次元に合わせて列を定義します。

Embeddingモデルと次元数

OpenAIのAPIで文章をベクトルにする場合、OpenAIのEmbeddingsガイドによると既定の次元は text-embedding-3-small が1536、text-embedding-3-large が3072です。dimensions パラメータで短くでき、このパラメータはtext-embedding-3以降のモデルだけが受け付けます。text-embedding-ada-002 もモデル一覧に残っていますが、次元を変えられないため新しく作るなら3系を選びます。テキストを外部に送れない場合やブラウザで完結させたい場合は、Transformers.jsでローカルのモデルを動かせばEmbeddingの生成から検索までPGliteと同じページの中で済みます。日本語に強いモデルの選び方は埋め込みモデルとは?仕組みと日本語モデルの選び方を参照してください。

距離演算子の選び方

pgvectorの距離演算子は次の4つが中心です(ほかにバイナリベクトル用の <~> ハミング距離と <%> ジャッカード距離があります)。PGlite 0.5.13とpgvector 0.8.7で、[1,0,0] を基準に a=[1,0,0]・b=[0.9,0.1,0]・e=[2,0,0] を比べた結果を並べます。

演算子 指標 a b e(向き同じ・長さ2倍)
<=> コサイン距離 0 0.0061 0
<-> ユークリッド距離(L2) 0 0.1414 1.0000
<#> 負の内積 -1.0000 -0.9000 -2.0000
<+> L1距離 0 0.2000 1.0000

コサイン距離だけが、向きが同じで長さが違う e を a と同じ「距離0」として扱います。文章のEmbeddingは向きに意味が表れるため、文章の検索では <=> を使います。<#> は小さいほど近くなるよう内積の符号を反転させた値で、長さの大きい e が先頭に来ます。OpenAIのEmbeddingのように長さ1に正規化されたベクトルなら、<#> とコサインで並び順は同じになります。演算子の意味を逆に書いた解説もあるため、pgvectorのREADMEで確認してください。計算式の背景はコサイン類似度とは?計算式・仕組みと類似検索での実装で解説しています。

類似度検索SQLとカテゴリでの絞り込み

以下は検索部分の抜粋です。文書を埋め込んでfaqsへ登録し、同じモデル・同じ次元数で作った検索文の数値配列をqueryEmbeddingに設定してから実行します。検索は距離の小さい順に並べます。類似度として表示するなら 1 - 距離 にします。

const q = JSON.stringify(queryEmbedding)   // '[0.012,-0.034,...]'
const { rows } = await db.query(
  `SELECT id, question, 1 - (embedding <=> $1) AS similarity
     FROM faqs
    WHERE category = $2
      AND embedding <=> $1 < 0.35
    ORDER BY embedding <=> $1
    LIMIT 3`,
  [q, '返品']
)

WHEREの < 0.35 は「十分に似た候補が無ければ0件にする」ための閾値です。適切な値はモデルとデータで変わるので、実際の問い合わせ文を数十件流して決めます。HNSWを追加した場合はカテゴリ条件で候補が減るため、全件走査の結果と比較し、必要に応じて hnsw.ef_search や反復スキャン(hnsw.iterative_scan)を調整します。キーワードの完全一致に弱い点を全文検索で補う設計は、ハイブリッド検索とは?RAGでBM25とベクトル検索を統合する仕組みで扱っています。

PGliteのHNSW性能測定と導入判断

pgvectorの近似検索インデックスHNSWはPGliteでも作れますが、構築には時間がかかります。384次元のランダムなベクトル1万件をメモリ上に置き、同じ検索ベクトルで各検索を3回実行した結果です(Intel Mac・Node.js 26.5.0・PGlite 0.5.13)。表の検索時間は3回の最小値〜最大値で、構築時間は1回の測定値です。

処理 時間
インデックスなし(全件走査)の上位5件検索 21〜71ミリ秒
HNSWインデックスの構築(vector_cosine_ops) 約45秒
参考:同じPCのネイティブのPostgreSQL 18.6での構築 約2.1秒
HNSWを使った上位5件検索 4〜22ミリ秒

1万件ではインデックスなしでも100ミリ秒を切りました。HNSWの構築は、maintenance_work_memが同じ64MBのネイティブのPostgreSQL 18.6(pgvector 0.8.7)より約20倍遅くなりました。約45秒はNode.jsでの測定値です。ブラウザのメインスレッドで構築するとその間ページの操作を妨げるため、重い処理はPGliteWorkerに移します。

インデックスを張るかどうかは件数で一律に決めず、実際の次元数・端末・検索頻度で全件走査の時間を測り、要求する応答時間を超えたときに検討します。通常のPostgreSQLへ移すかどうかも、応答時間、メモリ使用量、同時利用、運用管理の要件で判断します。張る場合は、pgvectorのREADMEにあるとおり vector 型のHNSWは2,000次元までです。3072次元のモデルを使うなら dimensions で次元を減らすか halfvec を検討します。検索で使う演算子とインデックスの演算子クラスも揃えます(<=> なら vector_cosine_ops)。揃えないとインデックスは使われません。HNSWのパラメータの決め方はHNSWとは?階層グラフの仕組みとM・ef_constructionの決め方を参照してください。

CREATE INDEX ON faqs USING hnsw (embedding vector_cosine_ops);

PGliteを使うべきでない場面

PGliteは、1つのプロセスから1本の接続で使うデータベースです。次の条件のどれかに当てはまるなら、最初から通常のPostgreSQL(Dockerやマネージドサービス)にします。

  • 複数のアプリサーバーやワーカーから同じデータに読み書きする(pglite-serverでも処理は1本ずつ順番になる)。
  • サーバー側で複数利用者のデータを共有し、自動フェイルオーバーやレプリケーションを必要とする。PGliteにはダンプと再読込のAPIがありますが、バックアップの保存・復元手順はアプリ側で設計する必要があります。
  • 同時更新・ロック・デッドロックの挙動を確かめるテスト。接続が1本なので再現できません。
  • 必要なPostgreSQL拡張をPGlite向けにビルド・動作確認できない。標準で提供される拡張は拡張一覧にあるものだけです。

反対に、PostgreSQLの機能(JSONB・全文検索・pgvector)をそのまま使える組み込みDBが欲しい場面では、SQLiteより本番との差が小さくなります。ブラウザのPGliteとサーバーのPostgreSQLを同期させたい場合は、同じ開発元のElectricSQLが組み合わせの候補です。社内文書の検索やRAGを本番の規模で組む段階では、RAG構築支援のように、PostgreSQLとpgvectorの設計から相談する選択肢もあります。

よくある質問

PGliteはDockerのPostgreSQLの代わりになりますか?

ローカル開発・単体テスト・ブラウザ内のアプリなら代わりになります。アプリから直接importするか、pglite-server を起動して通常のPostgreSQLとして接続します。ただし接続は順番に処理されるため、同時接続やロックの検証、本番用途には使えません。

PythonでPGliteは使えますか?

py-pgliteで使えます。用途はpytestのテストで、内部でNode.jsとnpmを使ってPGliteを起動するため、Node.jsのインストールが必要です。0.5.3が動かすのはPGlite 0.3系(PostgreSQL 17.4・pgvector 0.8.0)です。

@electric-sql/pglite/vector のimportがエラーになるのはなぜですか?

PGlite 0.5.0以降はpgvectorが別パッケージになったためです。npm install @electric-sql/pglite-pgvector を実行し、import { vector } from '@electric-sql/pglite-pgvector' に書き換えます。pglite-serverの --extensions も @electric-sql/pglite-pgvector:vector と書きます。

PGliteとSQLiteはどう違いますか?

どちらも組み込みのデータベースですが、PGliteの中身はPostgreSQL 18.6そのものです。本番がPostgreSQLなら、スキーマ・SQL・pgvectorの検索をそのままローカルで動かせます。SQLiteでベクトル検索をするにはsqlite-vecなど別の拡張を使い、SQLの書き方も変わります。

PGliteは本番環境で使えますか?

サーバー側の本番データベースとしては使えません。1本の接続しか持たず、複数プロセスからの共有やレプリケーションの仕組みがないためです。ブラウザやデスクトップアプリの中で、利用者ごとにローカルのデータを持つ用途なら本番でも使えます。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.06 テックブログ ドコモ不正アクセス:最大29.5万件の流出項目と見舞金、My docomo停止の経過と偽連絡の見分け方
  2. 2026.11.03 テックブログ ネットマイル不正アクセスで最大549万人:対象確認と退会会員データを消す実装
  3. 2026.11.06 テックブログ あいおいニッセイ同和の不正アクセス:44.4万件の漏えい項目と窓口、止まった手続きの代わり
  4. 2026.11.06 テックブログ アイリスオーヤマ情報漏洩:アイリスプラザ770万人分の対象項目と本物の連絡の見分け方
  5. 2026.07.09 テックブログ Muse Imageとは?Metaの画像生成AIの使い方・API料金(1枚0.01ドル)と日本での提供状況【2026年10月】

RELATED POSTS 関連記事

目次