ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PGlite 实战指南:在浏览器与 Node 中运行 Postgres 的 WASM 方案

2026/9/14 11:26:20 拓冰建站 浏览量
PGlite 实战指南:在浏览器与 Node 中运行 Postgres 的 WASM 方案 PGlite 实战指南在浏览器与 Node 中运行 Postgres 的 WASM 方案【免费下载链接】pgliteEmbeddable Postgres with real-time, reactive bindings.项目地址: https://gitcode.com/GitHub_Trending/pg/pglitePGlite 是 Electric 出品的 Postgres WASM 构建版本被封装为一个 TypeScript 客户端库让你可以在浏览器、Node.js、Bun 和 Deno 中直接运行真实的 PostgreSQL无需安装任何额外依赖。本文将围绕 README.md 的主体内容结合仓库源码packages/pglite深入讲解 PGlite 的安装方式、持久化机制、底层运行原理、扩展生态与构建贡献流程读完即可在自己的前端或服务端项目中落地一套嵌入式 Postgres 方案。PGlite 是什么WASM 化的 PostgresPGlite 是 PostgreSQL 的 WASMWebAssembly构建版本被打包进一个 TypeScript 客户端库中。它可以在浏览器、Node.js、Bun 和 Deno 中运行 Postgres不需要安装任何其他依赖如系统级 PostgreSQL、Docker 或虚拟机。按 README 的描述其体积约为 3MB gzippedpackages/pglite/package.json 中的 description 则标注为 3.7MB gzipped并且支持大量 Postgres 扩展包括 pgvector 和 PostGIS 等。与以往Postgres in the browser类项目不同PGlite不使用 Linux 虚拟机——它就是原汁原味的 Postgres 编译为 WASM。项目定位是在 Postgres 上直接构建 reactive、realtime、local-first 的应用即把完整的 SQL 能力带到任何 JavaScript 运行环境中。PGlite 有两种基本使用形态临时内存数据库ephemeral in-memory database进程内运行关闭即销毁适合测试、原型和无需持久化的场景持久化数据库浏览器端持久化到 IndexedDBNode/Bun/Deno 端持久化到文件系统。快速体验来自 README.md 的 Hello World 示例import { PGlite } from electric-sql/pglite; const db new PGlite(); await db.query(select Hello world as message;); // - { rows: [ { message: Hello world } ] }在浏览器中使用 PGlite浏览器中可以通过常规包管理器安装并导入import { PGlite } from electric-sql/pglite;也可以直接通过 CDN如 jsDelivr以 ESM 方式引入import { PGlite } from https://cdn.jsdelivr.net/npm/electric-sql/pglite/dist/index.js;内存模式不传任何参数创建实例即为纯内存数据库const db new PGlite() await db.query(select Hello world as message;) // - { rows: [ { message: Hello world } ] }持久化到 IndexedDB浏览器端要持久化数据使用idb://前缀指定数据目录即可const db new PGlite(idb://my-pgdata);首次创建时 PGlite 会自动执行 initdb 初始化数据目录再次打开同一个idb://路径时则直接恢复已有数据库。这一点可以从源码得到印证在 fs/index.ts 中parseDataDir会识别idb://前缀并选择IdbFsIndexedDB 文件系统而 pglite.ts 中会检查PGDATA/PG_VERSION是否存在来决定是resuming已有数据库还是运行 initdb 初始化。在 Node / Bun / Deno 中使用 PGlite安装运行时命令Node.jsnpm install electric-sql/pgliteBunbun install electric-sql/pgliteDenodeno add npm:electric-sql/pglite内存模式import { PGlite } from electric-sql/pglite; const db new PGlite(); await db.query(select Hello world as message;); // - { rows: [ { message: Hello world } ] }持久化到文件系统Node/Bun/Deno 端直接传入一个本地路径即可把数据库持久化到文件系统const db new PGlite(./path/to/pgdata);源码层面fs/index.ts 的parseDataDir逻辑如下file://前缀或任何不带前缀的路径都会走nodefsNode 文件系统idb://走 IndexedDBopfs-ahp://走浏览器 OPFS Access Handle Pool而空值或memory://前缀则使用内存文件系统。因此 Node 端不写前缀即为文件系统持久化浏览器端必须显式使用idb://或opfs-ahp://。工作原理为什么 Postgres 能跑在 WASM 上这是 PGlite 最核心的工程问题。PostgreSQL 传统上采用进程 fork 模型每当客户端发起连接服务端就 fork 出一个新进程来管理该连接。而 EmscriptenC 到 WebAssembly 的编译器编译出的程序无法 fork 新进程只能在单进程模式下运行。因此 Postgres 无法被直接编译成 WASM 以常规方式运行。幸运的是PostgreSQL 内置了一个单用户模式single user mode主要用于引导bootstrapping和恢复recovery等命令行场景。PGlite 正是基于这一能力在 Postgres 编译到 WASM 后于 JavaScript 环境中引入了一条输入/输出通路来与 Postgres 交互——即 README 所述PGlite introduces an input/output pathway。从源码看这条通路的落地方式如下pglite.ts 的defaultStartParamsstatic readonly defaultStartParams [ --single, // selects single-user mode (must be first argument) -F, // turn fsync off -O, // allow system table structure changes -j, // do not use newline as interactive query delimiter -c, search_pathpublic, -c, exit_on_errorfalse, -c, log_checkpointsfalse, -c, max_worker_processes0, -c, max_parallel_workers0, -c, max_parallel_workers_per_gather0, -c, io_methodsync, -c, max_parallel_maintenance_workers0, ]--single必须以第一个参数传入以启用单用户模式-F关闭 fsync、-O允许修改系统表结构、-j不使用换行作为交互式查询分隔符其余-c参数则关闭并行与 worker 进程以适应 WASM 的单线程环境。在运行时PGlite 通过electric-sql/pg-protocol仓库中的 packages/pg-protocol 包实现 Postgres 线协议wire protocol的解析与序列化。客户端调用query()时走的是扩展查询协议Parse → Describe → Bind → Execute → Sync见 base.ts 的#runQuery而exec()走简单查询协议base.ts 的#runExec。每个查询都会经过#queryMutex与#transactionMutex互斥锁串行执行保证单连接模型下的一致性。启动流程pglite.ts 的#init大致为解析数据目录并加载文件系统 → 后台预下载pglite.wasm、initdb.wasm与文件系统包pglite.data→ 创建 WASM 内存并实例化模块 → 如无现有数据库则先用独立的 initdb 实例初始化 → 以单用户模式启动 Postgres → 初始化数组类型与扩展。README 提到的screenshot.png即位于仓库根目录展示了实际运行效果。核心 API 与配置选项PGlite 实例的核心方法定义在 interface.ts 的PGliteInterface中主要包括query(query, params?, options?)执行单条 SQL扩展查询协议支持参数化sql\...模板字符串查询模板值自动作为参数templating.tsexec(query)执行可能包含多条语句的 SQL返回结果数组transaction(callback)事务封装回调内通过tx.query执行异常自动 ROLLBACKbase.tslisten(channel, callback)/onNotification(callback)基于LISTEN/NOTIFY的实时通知describeQuery(query)获取查询的参数类型与结果字段类型信息dumpDataDir(compression?)把整个 PGDATA 目录导出为 tarballgzip/auto/noneclose()关闭数据库并释放 WASM 运行时实例还实现了Symbol.asyncDispose支持await using语法。PGliteOptionsinterface.ts支持的常用配置项选项说明dataDir数据目录idb://前缀为 IndexedDBmemory://为内存其余为 Node 文件系统路径username/database启动后的默认用户与数据库名默认postgres会写入 WASM 环境变量并执行SET ROLEfs自定义文件系统实现实现Filesystem接口见 fs/base.tsdebug调试级别 05开启后输出协议与内部日志relaxedDurability放宽持久性要求异步执行文件系统同步以提升性能extensions扩展注册表值为扩展对象或扩展 bundle 的 URLloadDataDir从一个 tarballBlob/File加载已有数据库配合dumpDataDir使用icuDataDir自定义 ICU 数据包如 pglite-icu-fullinitialMemoryWASM 初始内存大小字节默认 128MB2048 页 × 64KB上限 32768 页pgliteWasmModule/initdbWasmModule预编译好的 WASM 模块可跳过网络下载fsBundle预加载的文件系统 bundlepglite.dataparsers/serializers按 Postgres OID 覆盖默认的类型解析/序列化器startParams/initDbStartParams覆盖 postgres / initdb 的启动参数postgresqlconf追加到postgresql.conf的配置内容字符串或数组此外PGlite.create()是静态工厂方法返回 Promise并能在 TypeScript 类型层面把扩展的命名空间合并到实例上pglite.ts配合扩展使用时更推荐这种方式。扩展生态从 pgvector 到 PostGISPGlite 的扩展体系由两部分组成内置 contrib 扩展contrib目录packages/pglite/src/contrib下提供了 30 余个 Postgres contrib 扩展的 TS 封装如amcheck、auto_explain、bloom、btree_gin、citext、cube、earthdistance、fuzzystrmatch、hstore、intarray、ltree、pg_trgm、pgcrypto、tablefunc、unaccent等并配有对应测试packages/pglite/tests/contrib。独立扩展包仓库以独立 npm 包形式维护了一批进阶扩展例如pglite-pgvector向量检索pglite-postgis地理空间pglite-age图数据库Apache AGEpglite-icu-full完整 ICU 数据pglite-pgmq、pglite-pg_uuidv7、pglite-pg_hashids、pglite-pg_ivm、pglite-pgtap 等。扩展的加载机制在 pglite.ts每个扩展要么提供 JSsetup函数可返回emscriptenOpts、namespaceObj、bundlePath、sharedPreloadLibraries、init、close要么直接给一个指向扩展 bundle 的URL。以 pgvector 为例packages/pglite-pgvector/src/index.ts 只是简单地把vector.tar.gz作为bundlePath交给 PGlite 加载const setup async (_pg: PGliteInterface, emscriptenOpts: any) { return { emscriptenOpts, bundlePath: new URL(../release/vector.tar.gz, import.meta.url), } satisfies ExtensionSetupResult }同时extensionUtils.ts 会在数据库初始化后把文件系统中存在的动态扩展编译并加载loadExtensions扩展的shared_preload_libraries也会被自动合并进postgresql.confpglite.ts。已知限制PGlite 是单用户/单连接的同一时刻只有一个连接所有查询通过互斥锁串行执行。因此它适合本地优先local-first、嵌入式场景而不适合当作多客户端并发的服务端数据库使用。README 明确列出了这一点PGlite is single user/connection.从源码构建 PGlite 并参与贡献PGlite 的构建分为两部分对应 packages/pglite 与 postgres-pglite 两个仓库/目录构建 Postgres WASM 模块构建 PGlite 客户端库及其余 TypeScript 包。构建 WASM 模块需要Docker构建 TypeScript 包需要Node.js v20 及以上以及pnpm包管理器。克隆与安装依赖注意需要用--recurse-submodules拉取子模块包含 Postgres 源码 forkgit clone --recurse-submodules https://github.com/electric-sql/pglite cd pglite pnpm install一键构建全部仓库根目录提供了便捷的pnpm build:all命令它会使用 Docker 构建 Postgres WASM 模块产物复制到packages/pglite/release按依赖顺序构建 PGlite 客户端库及其他 TypeScript 包。只构建 WASM 模块pnpm wasm:build如果不想从零编译 WASM 模块CI 会在每次 PR 成功合并后自动生成最新二进制去最近一次成功合并的 PR 下找到_Interim build files:_评论中的下载链接解压后放到本地仓库的packages/pglite/release目录即可。只构建 TypeScript 包pnpm ts:build该命令会按包之间的依赖关系以正确顺序构建所有包。之后可以进入任意单个包进行开发使用各自的build、test脚本以及stylecheck、typecheck保证代码风格与类型正确性。也可以单独构建一个包cd packages/pglite pnpm build提交 PR准备好提交 PR 时在仓库根目录运行pnpm changeset按提示创建合适的 changeset。凡是涉及代码的贡献都必须附带一个 changeset。许可证PGlite 采用双重许可Apache License 2.0见 LICENSE与 PostgreSQL License见 POSTGRES-LICENSE使用者可任选其一对 Postgres 源码postgres-pglite的修改则遵循 PostgreSQL License。PGlite 构建于 Neon 的 Stas Kelvich 对 Postgres 的 fork 之上README 的 Acknowledgments 部分对此有明确致谢。【免费下载链接】pgliteEmbeddable Postgres with real-time, reactive bindings.项目地址: https://gitcode.com/GitHub_Trending/pg/pglite创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考