---
title: "PGliteとは？Docker不要のPostgreSQLの使い方とpgvectorでの類似度検索【0.5系】"
url: "https://www.issoh.co.jp/tech/details/6351/"
published: 2025-04-17
updated: 2026-10-30
categories: ["データベース"]
publisher: "株式会社一創"
---

# 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:postgres@127.0.0.1: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](https://github.com/electric-sql/pglite)は「Node.js、Bun、Denoとブラウザで動き、gzipで3MB、pgvectorやPostGISなど多くの拡張に対応する」と説明しています。PostgreSQL本体のシングルユーザーモードをWASMから呼び出す作りで、Linuxの仮想マシンは使いません。ネイティブのPostgreSQLを別にインストールする必要もありません。[npmのダウンロード数API](https://api.npmjs.org/downloads/point/last-week/@electric-sql/pglite)によると、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](https://github.com/electric-sql/pglite/blob/main/packages/pglite/CHANGELOG.md)に「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](https://pglite.dev/docs/pglite-tools) を使って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の変更点](/tech/details/7939/)で扱っています。ブラウザの複数タブで1つのデータベースを共有したい場合は、[PGliteWorker](https://pglite.dev/docs/multi-tab-worker)で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://postgres@127.0.0.1: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:postgres@127.0.0.1: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のドキュメント](https://pglite.dev/docs/pglite-socket)も、通常のPostgreSQLとは違うためすべての使い方が動く保証はない、と注意しています。

### Docker・Testcontainersとの使い分け

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

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

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

PythonからPGliteを使うライブラリが [py-pglite](https://pypi.org/project/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）と使い方](/tech/details/6231/)にまとめています。

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

[PGliteの拡張一覧](https://pglite.dev/extensions/)のとおり、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ガイド](https://developers.openai.com/api/docs/guides/embeddings)によると既定の次元は `text-embedding-3-small` が1536、`text-embedding-3-large` が3072です。`dimensions` パラメータで短くでき、このパラメータはtext-embedding-3以降のモデルだけが受け付けます。`text-embedding-ada-002` もモデル一覧に残っていますが、次元を変えられないため新しく作るなら3系を選びます。テキストを外部に送れない場合やブラウザで完結させたい場合は、Transformers.jsでローカルのモデルを動かせばEmbeddingの生成から検索までPGliteと同じページの中で済みます。日本語に強いモデルの選び方は[埋め込みモデルとは？仕組みと日本語モデルの選び方](/tech/details/16044/)を参照してください。

### 距離演算子の選び方

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](https://github.com/pgvector/pgvector)で確認してください。計算式の背景は[コサイン類似度とは？計算式・仕組みと類似検索での実装](/tech/details/15075/)で解説しています。

### 類似度検索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とベクトル検索を統合する仕組み](/tech/details/8014/)で扱っています。

## 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](https://pglite.dev/docs/multi-tab-worker)に移します。

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

```
CREATE INDEX ON faqs USING hnsw (embedding vector_cosine_ops);
```

## PGliteを使うべきでない場面

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

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

反対に、PostgreSQLの機能（JSONB・全文検索・pgvector）をそのまま使える組み込みDBが欲しい場面では、SQLiteより本番との差が小さくなります。ブラウザのPGliteとサーバーのPostgreSQLを同期させたい場合は、同じ開発元の[ElectricSQL](/tech/details/7220/)が組み合わせの候補です。社内文書の検索やRAGを本番の規模で組む段階では、[RAG構築支援](/service/ai/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本の接続しか持たず、複数プロセスからの共有やレプリケーションの仕組みがないためです。ブラウザやデスクトップアプリの中で、利用者ごとにローカルのデータを持つ用途なら本番でも使えます。

## 関連記事

- [pgvectorとは？インストール手順（Windows・Ubuntu・Docker）と使い方【0.8.7対応】](/tech/details/6231/)
- [ElectricSQLとは？Postgres同期エンジンの仕組み・導入手順と2026年の変化](/tech/details/7220/)
- [Testcontainersとは？Dockerで作る使い捨てテスト環境と2.0系の移行点](/tech/details/4629/)
- [コサイン類似度とは？計算式・仕組みと類似検索での実装を実装目線で解説](/tech/details/15075/)
- [Drizzle ORM（ドリズル）とは？使い方・Prismaとの比較・v1.0 RCの変更点【2026年10月】](/tech/details/7939/)

---

出典: [PGliteとは？Docker不要のPostgreSQLの使い方とpgvectorでの類似度検索【0.5系】](<https://www.issoh.co.jp/tech/details/6351/>)（株式会社一創）
