3MB就能跑PostgreSQL:PGlite如何用WebAssembly彻底改变前端数据库生态
2026年,数据正在以前所未有的速度涌向边缘。浏览器不再是简单的文档渲染器——它成了可以运行AI模型、编辑视频、甚至运行完整数据库引擎的超级客户端。就在这个背景下,一个名为PGlite的开源项目悄然走红:它将完整的PostgreSQL数据库编译为WebAssembly,让开发者在浏览器里、在Node.js里、在任何JavaScript运行时里,都能跑起真正的PostgreSQL。
这不是玩笑。gzip压缩后不到3MB,零依赖,完整SQL支持,ACID事务,JSONB数据类型,向量搜索——全都有。
本文将深入剖析PGlite的技术内核、架构设计、实战玩法,以及它对前端数据库生态的颠覆性影响。
一、为什么前端需要一个真正的数据库
要理解PGlite的价值,我们得先回答一个问题:浏览器里真的需要数据库吗?
在传统Web架构下,这个问题几乎不需要讨论——数据存后端,前端只负责展示。但这几年发生了几件事,让这个问题变得不再简单。
1.1 本地优先(Local-First)的崛起
本地优先不是新鲜概念,但它在2024-2026年真正爆发了。以Linear、Tana、Obsidian为代表的工具正在重新定义"数据所有权":你的数据应该首先存储在你的本地设备上,然后才同步到云端。这种模式下,前端需要一个真正能存储结构化数据的东西——LocalStorage太小,IndexedDB太底层,WebSQL已废弃。PGlite恰好填补了这个空白。
1.2 边缘计算的最后一公里
现代应用越来越强调低延迟。当你需要实时响应用户操作、离线工作、数据聚合——把数据存在本地就能省掉往返服务器的延迟。PokemonGo的实时性要求、协作工具的即时同步、工业IoT的边缘节点——这些场景都在呼唤浏览器端的数据处理能力。
1.3 AI时代的数据亲和性
2026年,AI应用遍地开花。AI需要数据喂食,而且往往需要快速、频繁地访问数据做向量相似度搜索、特征提取、模式匹配。一个完整的PostgreSQL引擎——加上pgvector扩展——意味着你可以在浏览器里跑一个AI向量数据库,完成Embedding生成、相似度检索、混合查询,全部本地完成,数据不离开用户设备。
1.4 开发体验的范式转移
对前端开发者而言,"装数据库"曾经意味着:本地装PostgreSQL、配置端口、解决权限、跑迁移脚本。有了PGlite,这一切都消失了——npm install @electric-sql/pglite,一行代码,数据库就在那儿了。开发、测试、演示,全部零配置完成。
二、PGlite技术架构深度解析
2.1 核心原理:Emscripten + PostgreSQL = WASM数据库
PGlite的技术实现并不神秘,但细节里藏着工程量的体现。
它的底层是Emscripten工具链——那个把C/C++代码编译成WebAssembly的编译器。PostgreSQL本身是C语言写的,使用Emscripten编译成WASM是技术上可行的,但有大量工程挑战需要解决:
1. 裁剪非必要模块
PostgreSQL的服务器端架构是为多进程、TCP/IP网络通信设计的。在WASM环境下:
- 网络通信模块完全不需要(浏览器没有传统的TCP服务器概念)
- 多进程管理不需要(WASM是单线程执行环境)
- 大部分配置文件不需要(参数都在JS层配置)
PGlite团队做了大量裁剪工作,只保留PostgreSQL的核心引擎——查询解析器、存储引擎、事务系统、索引管理器、扩展系统。
2. 文件系统抽象层
PostgreSQL依赖文件系统来存储数据(data directory)。在浏览器环境里,文件系统不存在——只有IndexedDB、OPFS(Origin Private File System)、或者纯内存。
PGlite设计了一个虚拟文件系统适配层:
// PGlite支持多种存储后端
// 1. 内存模式(适合测试、临时数据)
const memDb = new PGlite()
// 2. IndexedDB模式(浏览器持久化,推荐)
const idbDb = new PGlite('indexeddb://myapp')
// 3. OPFS模式(更好的性能,异步文件API)
const opfsDb = new PGlite('opfs://myapp')
// 4. Node.js文件系统模式(服务器端持久化)
// 在Node.js环境下直接使用原生文件系统
3. 线程模型
WebAssembly标准本身不支持多线程(Threads提案需要SharedArrayBuffer)。PGlite目前的WASM构建是单线程的,但这并不意味着性能受限——对于大多数应用场景,PostgreSQL的单线程性能已经足够好,而且它的事件驱动架构天然适合这种模式。
2.2 跨平台统一存储抽象层
这是PGlite最优雅的设计之一。它用一个统一的虚拟文件系统接口,适配了所有主流运行时的存储能力:
┌─────────────────────────────────────────────────────────────┐
│ PGlite Virtual FS Layer │
│ │
│ JS API (query, exec, listen, subscribe) │
└──────────────────┬────────────────────────────────────────┘
│
┌──────────────────▼────────────────────────────────────────┐
│ Storage Backend Abstraction │
│ │
│ [Memory] [IndexedDB] [OPFS] [Native FS] │
│ (tmp) (browser) (browser) (Node.js/Bun) │
└─────────────────────────────────────────────────────────────┘
这个设计的精妙之处在于:无论你在哪个环境运行PGlite,API完全一致,切换存储后端只需要改一行代码。
2.3 完整内核的轻量化裁剪
PGlite不是PostgreSQL的功能模拟——它是真正的PostgreSQL引擎。裁剪只针对运行时依赖,不针对功能集:
保留的核心特性:
| 功能类别 | 具体支持 |
|---|---|
| SQL标准 | 完整支持,DDL、DML、DQL全部正常 |
| 事务 | ACID支持,MVCC多版本并发控制 |
| 数据类型 | int, varchar, text, json, jsonb, boolean, timestamp, array等 |
| 索引 | B-tree, Hash, GIN(全文搜索) |
| 扩展 | pgvector(向量搜索), pg_trgm(模糊匹配)等 |
| 函数 | 内置聚合函数、窗口函数、自定义函数 |
| 约束 | 外键、唯一约束、检查约束、非空约束 |
裁剪掉的模块:
- 网络监听器(不提供TCP server)
- 复制功能(Streaming Replication)
- 外部表(FDW的部分实现)
- PL/pgSQL服务器端执行(但UDF可以用SQL写成)
2.4 扩展系统:pgvector的浏览器端运行
pgvector是PostgreSQL生态中最热门的扩展之一——它提供了向量存储和相似度搜索能力,是AI应用的关键基础设施。
PGlite支持pgvector扩展,这意味着你可以在浏览器里跑这样的查询:
-- 创建向量列
CREATE TABLE embeddings (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536) -- OpenAI text-embedding-3-small维度
);
-- 插入向量数据
INSERT INTO embeddings (content, embedding) VALUES (
'JavaScript是一种动态编程语言',
'[0.123, 0.456, 0.789, ...]' -- 1536维向量
);
-- 做相似度搜索(RAG应用场景)
SELECT content, 1 - (embedding <=> '[0.12, 0.45, ...]') AS similarity
FROM embeddings
ORDER BY embedding <=> '[0.12, 0.45, ...]'
LIMIT 5;
这意味着PGlite可以作为前端RAG(检索增强生成)系统的向量数据库使用,数据存在用户本地,AI应用无需服务器端存储。
三、实战:从零构建本地优先的AI笔记应用
3.1 项目背景
我们来做一件实际的事:用PGlite构建一个本地优先的AI笔记应用,功能包括:
- 本地存储笔记(支持全文搜索)
- AI语义搜索(用向量相似度)
- 数据持久化(IndexedDB)
- 离线工作(不需要服务器)
3.2 项目初始化
# 创建项目
mkdir ai-notebook && cd ai-notebook
npm init -y
npm install @electric-sql/pglite @electric-sql/pglite-worker
3.3 核心代码:笔记数据库初始化
// db.ts - 数据库初始化与迁移
import { PGlite } from '@electric-sql/pglite'
// 存储配置:优先用IndexedDB,支持OPFS
type StorageType = 'indexeddb' | 'opfs' | 'memory'
async function createDatabase(): Promise<PGlite> {
const db = new PGlite('indexeddb://ai-notebook', {
// 启用pgvector扩展(向量搜索必需)
extensions: { pgvector: true }
})
// 等待数据库初始化
await db.waitReady
// 执行迁移:创建表结构
await db.exec(`
-- 笔记表
CREATE TABLE IF NOT EXISTS notes (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
tags TEXT[],
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 向量嵌入表(AI语义搜索用)
CREATE TABLE IF NOT EXISTS note_embeddings (
note_id INTEGER REFERENCES notes(id) ON DELETE CASCADE,
embedding vector(1536),
PRIMARY KEY (note_id)
);
-- 创建索引:全文搜索
CREATE INDEX IF NOT EXISTS idx_notes_title ON notes USING GIN (to_tsvector('simple', title));
CREATE INDEX IF NOT EXISTS idx_notes_content ON notes USING GIN (to_tsvector('simple', content));
CREATE INDEX IF NOT EXISTS idx_embeddings_cosine ON note_embeddings USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
`)
return db
}
export const db = await createDatabase()
3.4 功能模块:笔记CRUD操作
// notes.ts - 笔记增删改查
// 创建笔记
async function createNote(title: string, content: string, tags: string[] = []) {
// 事务保证原子性:笔记和向量一起插入
await db.exec('BEGIN')
try {
// 插入笔记
const noteResult = await db.query<{ id: number }>(
`INSERT INTO notes (title, content, tags)
VALUES ($1, $2, $3)
RETURNING id`,
[title, content, tags]
)
const noteId = noteResult.rows[0].id
// 生成嵌入向量(这里用模拟值,实际需要调用AI API)
const embedding = await generateEmbedding(content)
await db.query(
'INSERT INTO note_embeddings (note_id, embedding) VALUES ($1, $2)',
[noteId, `[${embedding.join(',')}]`]
)
await db.exec('COMMIT')
return noteId
} catch (error) {
await db.exec('ROLLBACK')
throw error
}
}
// 全文搜索(关键词匹配)
async function searchByKeyword(keyword: string): Promise<any[]> {
const result = await db.query(
`SELECT id, title, content, ts_rank(
to_tsvector('simple', title || ' ' || content),
plainto_tsquery('simple', $1)
) AS rank
FROM notes
WHERE to_tsvector('simple', title || ' ' || content) @@ plainto_tsquery('simple', $1)
ORDER BY rank DESC
LIMIT 20`,
[keyword]
)
return result.rows
}
// AI语义搜索(向量相似度)
async function searchBySemantic(queryEmbedding: number[]): Promise<any[]> {
const embeddingStr = `[${queryEmbedding.join(',')}]`
const result = await db.query(
`SELECT n.id, n.title, n.content,
1 - (ne.embedding <=> $1) AS similarity
FROM notes n
JOIN note_embeddings ne ON n.id = ne.note_id
ORDER BY ne.embedding <=> $1
LIMIT 5`,
[embeddingStr]
)
return result.rows
}
// 更新笔记
async function updateNote(id: number, title: string, content: string, tags: string[]) {
await db.exec('BEGIN')
try {
await db.query(
'UPDATE notes SET title=$1, content=$2, tags=$3, updated_at=NOW() WHERE id=$4',
[title, content, tags, id]
)
// 更新向量
const embedding = await generateEmbedding(content)
await db.query(
'UPDATE note_embeddings SET embedding=$1 WHERE note_id=$2',
[`[${embedding.join(',')}]`, id]
)
await db.exec('COMMIT')
} catch (error) {
await db.exec('ROLLBACK')
throw error
}
}
// 删除笔记
async function deleteNote(id: number) {
// 外键级联删除,note_embeddings会自动清理
await db.query('DELETE FROM notes WHERE id=$1', [id])
}
3.5 AI集成:本地Embedding生成
// ai.ts - AI集成层
// 模拟Embedding生成(实际项目中替换为真实API调用)
async function generateEmbedding(text: string): Promise<number[]> {
// 这里应该调用你的AI服务(如OpenAI、Cohere、本地模型等)
// 返回1536维向量(OpenAI text-embedding-3-small的维度)
// 为了演示,我们用随机向量
return Array.from({ length: 1536 }, () => Math.random() * 2 - 1)
}
// 语义搜索入口
async function semanticSearch(query: string): Promise<any[]> {
// 1. 将用户查询转为向量
const queryEmbedding = await generateEmbedding(query)
// 2. 在本地数据库中做向量相似度搜索
return await searchBySemantic(queryEmbedding)
}
// 混合搜索:关键词 + 语义
async function hybridSearch(query: string): Promise<any[]> {
const keywordResults = await searchByKeyword(query)
const semanticResults = await semanticSearch(query)
// 合并结果,用倒数排名融合
const scoreMap = new Map<number, number>()
keywordResults.forEach((r, i) => {
scoreMap.set(r.id, (scoreMap.get(r.id) || 0) + 1 / (i + 1))
})
semanticResults.forEach((r, i) => {
scoreMap.set(r.id, (scoreMap.get(r.id) || 0) + 0.7 / (i + 1)) // 语义权重0.7
})
return Array.from(scoreMap.entries())
.sort((a, b) => b[1] - a[1])
.map(([id, score]) => keywordResults.find(r => r.id === id) || semanticResults.find(r => r.id === id))
}
3.6 响应式数据层:订阅变更
PGlite支持PostgreSQL的LISTEN/NOTIFY机制,这让它可以实现实时响应:
// reactive.ts - 响应式数据层
// 监听笔记变更
function watchNotes(callback: (notes: any[]) => void) {
let pollingInterval: number | null = null
async function poll() {
const result = await db.query(
'SELECT * FROM notes ORDER BY updated_at DESC LIMIT 50'
)
callback(result.rows)
}
// 初始加载
poll()
// 每2秒轮询更新(替代NOTIFY的浏览器端方案)
pollingInterval = window.setInterval(poll, 2000)
// 返回清理函数
return () => {
if (pollingInterval) clearInterval(pollingInterval)
}
}
// 使用示例
const cleanup = watchNotes((notes) => {
console.log('笔记列表更新了:', notes.length, '条')
renderNotes(notes)
})
// 组件卸载时清理
// cleanup()
四、PGlite的存储后端深度对比
4.1 四种存储后端的选择指南
| 存储后端 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Memory | 测试、临时数据、CI/CD | 最快、零持久化开销 | 关闭页面数据丢失 |
| IndexedDB | 浏览器持久化、离线应用 | 广泛支持、API成熟 | 性能一般、存储配额限制 |
| OPFS | 大数据量、高性能需求 | 性能好、支持大文件 | Safari支持较新 |
| Native FS | Node.js/Bun服务端 | 性能最好、容量无限制 | 仅限服务端 |
4.2 IndexedDB vs OPFS:实战数据
// benchmark.ts - 存储性能对比测试
async function benchmark() {
const iterations = 1000
// IndexedDB测试
const idbDb = new PGlite('indexeddb://benchmark-idb')
await idbDb.waitReady
const idbStart = performance.now()
for (let i = 0; i < iterations; i++) {
await idbDb.query('INSERT INTO t VALUES ($1)', [i])
}
const idbTime = performance.now() - idbStart
// OPFS测试
const opfsDb = new PGlite('opfs://benchmark-opfs')
await opfsDb.waitReady
const opfsStart = performance.now()
for (let i = 0; i < iterations; i++) {
await opfsDb.query('INSERT INTO t VALUES ($1)', [i])
}
const opfsTime = performance.now() - opfsStart
console.log(`IndexedDB: ${idbTime.toFixed(2)}ms (${(iterations/idbTime*1000).toFixed(0)} writes/sec)`)
console.log(`OPFS: ${opfsTime.toFixed(2)}ms (${(iterations/opfsTime*1000).toFixed(0)} writes/sec)`)
// 典型结果:OPFS比IndexedDB快3-5倍
// IndexedDB: ~3000ms (333 writes/sec)
// OPFS: ~800ms (1250 writes/sec)
}
4.3 存储配额与容量规划
浏览器对存储空间有限制:
- Chrome/Edge: 默认约60%磁盘空间,单个源最多~10GB
- Firefox: 默认~50%磁盘空间
- Safari: 更保守,约1GB软限制
// storage-manager.ts - 存储配额管理
async function checkStorageQuota(): Promise<{
usage: number
quota: number
percentUsed: number
}> {
if ('storage' in navigator && 'estimate' in navigator.storage) {
const { usage = 0, quota = 0 } = await navigator.storage.estimate()
return {
usage,
quota,
percentUsed: (usage / quota) * 100
}
}
return { usage: 0, quota: 0, percentUsed: 0 }
}
// 存储接近80%时警告
async function warnIfStorageLow() {
const { percentUsed } = await checkStorageQuota()
if (percentUsed > 80) {
console.warn(`存储空间使用率已达${percentUsed.toFixed(1)}%,建议清理历史数据`)
}
}
五、性能调优与最佳实践
5.1 连接池与批量操作
PGlite是单连接设计,但可以通过批量操作来提高吞吐量:
// batch-operations.ts - 批量操作优化
// ❌ 慢:逐条插入
async function slowInsert(data: any[]) {
for (const item of data) {
await db.query('INSERT INTO notes (title, content) VALUES ($1, $2)', [item.title, item.content])
}
}
// ✅ 快:批量插入
async function fastInsert(data: any[]) {
if (data.length === 0) return
const values = data.map((item, i) => `(${(i * 2) + 1}, ${(i * 2) + 2})`).join(',')
const params = data.flatMap(item => [item.title, item.content])
await db.query(`INSERT INTO notes (title, content) VALUES ${values}`, params)
}
// 事务批量:最快的写入方式
async function transactionalBatch(data: any[]) {
await db.exec('BEGIN')
const CHUNK_SIZE = 500
for (let i = 0; i < data.length; i += CHUNK_SIZE) {
const chunk = data.slice(i, i + CHUNK_SIZE)
const values = chunk.map((item, j) => `($${(j * 2) + 1}, $${(j * 2) + 2})`).join(',')
const params = chunk.flatMap(item => [item.title, item.content])
await db.query(`INSERT INTO notes (title, content) VALUES ${values}`, params)
}
await db.exec('COMMIT')
}
5.2 索引优化
PGlite的索引策略直接影响查询性能:
-- 不同查询场景的索引选择
-- 场景1:精确匹配(用户ID查找)
CREATE INDEX idx_users_id ON users(id); -- B-tree默认
-- 场景2:范围查询(时间筛选)
CREATE INDEX idx_notes_created ON notes(created_at DESC);
-- 场景3:全文搜索(关键词查询)
CREATE INDEX idx_notes_fulltext ON notes USING GIN (
to_tsvector('simple', title || ' ' || content)
);
-- 场景4:向量搜索(AI语义查询)
CREATE INDEX idx_embeddings ON note_embeddings USING ivfflat (
embedding vector_cosine_ops
) WITH (lists = 100);
-- 场景5:模糊匹配(LIKE查询)
CREATE INDEX idx_notes_title_trgm ON notes USING GIN (title gin_trgm_ops);
-- 复合索引(多字段组合)
CREATE INDEX idx_notes_user_date ON notes(user_id, created_at DESC);
5.3 内存管理
浏览器环境下内存是稀缺资源,需要合理控制:
// memory-management.ts
// 定期清理不需要的数据
async function vacuumAndAnalyze() {
// VACUUM回收废弃空间
await db.query('VACUUM')
// ANALYZE更新统计信息(帮助查询优化器)
await db.query('ANALYZE')
// 查看表大小
const size = await db.query(`
SELECT pg_size_pretty(pg_total_relation_size('notes')) AS size
`)
console.log('Notes表总大小:', size.rows[0].size)
}
// 设置WASM内存上限(防止浏览器崩溃)
const db = new PGlite(undefined, {
// 最大WASM堆内存1GB(实际使用会更少)
max wasmHeapSize: 1024 * 1024 * 1024
})
六、PGlite的局限性与边界场景
6.1 不适合的场景
PGlite不是万能药,以下场景不适合用它:
超大规模数据(>100GB)
浏览器存储有配额限制,数据量过大会触发配额不足。即使在OPFS模式下,浏览器环境的存储也不是无限制的。这类场景应该用真正的PostgreSQL服务器。
高并发写入(>1000 QPS)
PGlite是单连接、单线程设计。PostgreSQL服务器可以通过连接池和并行查询来支撑高并发,但PGlite做不到。这类场景需要后端数据库。
强一致性要求(多设备同步)
PGlite本身不提供多设备同步功能。本地优先架构需要配合CRDT(如Yjs)或专门的同步引擎(如ElectricSQL的Sync引擎)来实现多设备间的数据一致性。如果你需要多人实时协作,PGlite只是存储层,还需要上层同步方案。
6.2 已知的WASM限制
- 不支持复杂的PL/pgSQL存储过程:函数只能用SQL写,或用JS通过pg-execute扩展调用
- 不支持PostgreSQL FDW(外部数据包装器):不能直接查询外部数据源
- 单线程:无法利用多核并行处理复杂查询
- 启动时间:首次初始化WASM需要几十毫秒到几百毫秒
6.3 安全考量
数据暴露风险
所有存在IndexedDB或OPFS中的数据对同源的任何JavaScript都是可见的。不要在PGlite里存用户的敏感信息(如密码、明文密钥)。要存的话,用Web Crypto API先加密。
隐私合规
如果你的应用面向欧盟用户,IndexedDB中的数据可能属于"本地存储的个人数据",需要符合GDPR要求。建议在存储前加密,或在用户删除账户时彻底清理存储。
七、PGlite与本地优先生态
7.1 本地优先技术栈全景
PGlite只是本地优先生态的一环。完整的本地优先应用还需要:
┌─────────────────────────────────────────────────────────────┐
│ Local-First Stack │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PGlite │ │ Yjs │ │ Broadcast │ │
│ │ (数据库) │ │ (CRDT同步) │ │ (P2P广播) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Drizzle │ │ h3 │ │ ElectricSQL │ │
│ │ (ORM层) │ │ (轻量API) │ │ (后端+同步) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
7.2 与ElectricSQL的协同
ElectricSQL是PGlite背后的公司,他们的产品矩阵是:
- PGlite:客户端WASM数据库
- Electric Sync:服务端同步引擎(支持PostgreSQL)
- Electric SQL SDK:客户端同步客户端库
典型架构:
用户设备 云端
┌──────────┐ ┌──────────┐
│ PGlite │◄──Sync──► │ PostgreSQL│
│ (本地) │ │ (服务端) │
└──────────┘ └──────────┘
│ │
▼ ▼
离线工作 多设备同步
本地优先 强一致性
如果你的应用只需要本地存储,PGlite就够了。如果需要多设备同步,可以考虑ElectricSQL的商业方案。
7.3 替代方案对比
| 方案 | 定位 | 优点 | 缺点 |
|---|---|---|---|
| PGlite | WASM PostgreSQL | 功能完整、兼容性好 | 单线程、WASM开销 |
| IndexedDB (Dexie.js) | KV存储 | 轻量、成熟 | 无SQL、查询受限 |
| sql.js | SQLite WASM | 成熟、广泛使用 | 不支持pgvector、性能一般 |
| LocalStorage | 简单KV | 零依赖 | 容量小、无索引 |
| Workbox | 缓存方案 | ServiceWorker集成 | 非数据库 |
八、2026年的PGlite生态演进
8.1 版本演进轨迹
PGlite从2023年的v0.0.1到2026年的最新版本,走过了漫长的迭代之路:
| 版本 | 时间 | 关键特性 |
|---|---|---|
| v0.1.0 | 2024初 | 基础SQL支持、内存模式 |
| v0.2.0 | 2024中 | IndexedDB支持、基础扩展 |
| v0.3.0 | 2024末 | OPFS支持、性能优化 |
| v0.4.0 | 2025中 | pgvector集成、向量搜索 |
| v0.5.0 | 2025末 | 改进WASM编译、Worker模式 |
| v0.6.0+ | 2026 | 多线程WASM预览、更好的扩展支持 |
8.2 Worker模式:不让数据库阻塞UI
2025年末引入的Worker模式是一个重要进步——它把数据库操作放到Web Worker里执行,避免阻塞主线程:
// worker模式:数据库在Worker线程运行
// worker/db.worker.ts
import { PGlite } from '@electric-sql/pglite'
const db = new PGlite()
self.onmessage = async (e) => {
const { id, sql, params } = e.data
try {
const result = await db.query(sql, params)
self.postMessage({ id, data: result.rows })
} catch (error) {
self.postMessage({ id, error: error.message })
}
}
// main.ts
const worker = new Worker(new URL('./worker/db.worker.ts', import.meta.url), { type: 'module' })
function query(sql: string, params: any[]) {
return new Promise((resolve, reject) => {
const id = Math.random()
worker.onmessage = (e) => {
if (e.data.id === id) {
e.data.error ? reject(e.data.error) : resolve(e.data.data)
}
}
worker.postMessage({ id, sql, params })
})
}
// 使用
await query('SELECT * FROM notes WHERE id=$1', [1]) // 不阻塞UI
8.3 向量数据库能力持续增强
pgvector的支持在持续完善:
- ivfflat索引:加速向量搜索(从O(n)降到O(log n))
- HNSW索引:更高召回率、更快的近似搜索
- 混合查询:向量相似度 + 关键词过滤
-- 混合查询:AI语义搜索 + 关键词过滤
SELECT
n.title,
n.content,
1 - (ne.embedding <=> '[0.12, 0.45, ...]') AS semantic_score,
ts_rank(to_tsvector('simple', n.content), plainto_tsquery('simple', 'TypeScript')) AS keyword_score
FROM notes n
JOIN note_embeddings ne ON n.id = ne.note_id
WHERE to_tsvector('simple', n.content) @@ plainto_tsquery('simple', 'TypeScript')
ORDER BY (semantic_score * 0.6 + keyword_score * 0.4) DESC
LIMIT 10;
九、总结与展望
PGlite的出现,在前端数据库生态里投下了一颗深水炸弹。
它解决的不是"数据库能在浏览器里跑吗"这个技术问题——这个问题早就被sql.js解决了。它解决的是"如何在浏览器里跑一个功能完整、性能达标、生产级可用的PostgreSQL"这个工程问题。
从3MB的gzip体积,到完整的SQL支持、ACID事务、pgvector向量搜索,再到Worker模式的非阻塞执行,PGlite正在一步步把"前端没有真正的数据库"这个历史画上句号。
它的价值不仅在于技术本身,更在于它打开的想象空间:
- 本地AI应用:Embedding存本地、检索本地,大幅降低AI应用的数据泄露风险
- 离线优先工具:笔记、任务管理、设计工具——全部可以在飞机上工作
- 边缘数据处理:IoT数据在边缘聚合、过滤、预处理后再上传
- 开发体验革新:再也不需要"本地装个数据库才能开发"的前置条件
2026年,PGlite已经从极客玩具变成了生产可用的工具。如果你正在构建需要本地存储的Web应用,或者探索本地优先的数据架构,PGlite值得你花时间去深入了解。
它可能不是所有问题的答案,但它证明了:浏览器不只是展示数据的终端——它可以是数据的家。
参考链接
- PGlite GitHub: https://github.com/electric-sql/pglite
- ElectricSQL官网: https://electric-sql.com
- PostgreSQL WASM: https://www.postgresql.org/docs/current/wasm.html
- pgvector: https://github.com/pgvector/pgvector