Postgres WASM:在浏览器里跑一个完整 PostgreSQL
阅读时间: 大约 10 分钟
Postgres WASM:在浏览器里跑一个完整 PostgreSQL

2022 年 10 月 3 日,Supabase 的 Mark Burggraf 与 Snaplet 一起开源了 postgres-wasm:一个完整跑在浏览器里的 PostgreSQL 服务器,支持把状态持久化到浏览器、从 pg_dump 恢复,甚至从远端数据库做逻辑复制。它不是第一个吃螃蟹的——Crunchy Data 一个月前就在 HN 上秀过类似的东西,但 Supabase/Snaplet 这次交出的是一个开源版本。本文基于当时的官方公告,拆解它到底怎么做到的、为什么这么做,以及它今天的定位。
一、为什么要在浏览器里跑 Postgres
先看它官方演示长什么样。下图是 wasm.supabase.com 的真实界面:一个浏览器标签页里开着 psql,建表、插数据、\dt 看表结构,右侧是 Network(Start/Stop)、文件传输、把 VM 状态存成 state.bin 或写进 IndexedDB 的控制面板。

官方列的用途方向:文档教程与 Demo、离线缓存(对标 sql.js / absurd-sql)、离线数据分析看板、测试 Postgres 函数/触发器/逻辑复制、开发环境(从生产拉数据或推变更)、数据库快照分发给同事或支持人员。但他们自己也很清醒:“目前大概 30MB,现阶段不适合通用场景”,只是潜力大。
二、关键决策:它其实不是”纯 WASM”
这是最容易被名字误导的一点。公告里明确说:团队一开始尝试过把 Postgres 源码直接编译成 WASM,但”比预想的复杂得多”。他们参考了 Crunchy Data 的思路,转而选择在浏览器里虚拟一台完整的机器:
- VM 层:用 Buildroot做一个裁剪过的 Linux,里面装好 Postgres;
- 运行时层:用 v86在浏览器里模拟一颗 x86 兼容 CPU 和整套硬件,把这台 Linux 跑起来;
- 网络代理层:浏览器里的 VM 没法直接被外部连接,于是用一个 websockproxy 的 fork 做流量中转。
换句话说,它不是”Postgres 编译成 WASM”,而是”一台 x86 Linux 虚拟机(里装着 Postgres)通过 v86 模拟在浏览器里运行”。这两者的性能与体积天差地别——这也是为什么它要 30MB 而不是 sql.js 那种几 MB。
三、三个工程难点
1. PG14 启动即段错误。 用旧版 Buildroot 先跑通了 PG13.3,但 PG14+ 一启动就 segfault。折腾了一轮后,v86 作者 Fabian 建议关掉 v86 的 JIT 编译,定位为 v86 的一个 bug;同时把 Postgres 的内存管理从 posix 切到 sysv 也绕过了问题。
2. 启动体积与启动速度。 哪怕尽力压缩,带 Postgres 的快照最初也超过 30MB。他们的解法是:只启动一个极简 Linux,其余文件通过 9P 文件系统在初始化后按需从 HTTPS 动态拉取;再加上内核参数 page_poison=on——让 Linux 在释放内存时写固定字节而非随机字节,未使用的内存块因此能被压得更小。最终压缩后的初始状态约 12MB,而且是一个”已经跑着 Postgres 14.4、psql 已加载、网络状态就绪”的状态。他们特意选择”预置已运行的 Postgres”而不是”更干净但每次刷新都要重新初始化”,换来了更快的体感。
3. 网络:浏览器只说 80/443,Postgres 只说 5432。 出于安全,浏览器不允许任意 TCP 端口;WASM 模块本身也碰不到外部网络。他们的方案是让 VM 内的模拟网卡(ne2k-pci)走 websocket,由一个外部代理把 websocket 流量转成 TCP/IP。

上图就是这套网络设计:浏览器里 v86 模拟器跑着 Postgres(容器内 5432),通过 proxy.wasm.supabase.com 做端口映射——比如内网 IP 10.5.6.177 被映射到代理的 6177 端口。这样你在自己电脑的终端里就能 psql postgres://postgres:***@proxy.wasm.supabase.com:6177,连上一个跑在别人浏览器里的 Postgres;甚至可以用 Postgres 的逻辑复制,把本地浏览器里的库和一个远端 Supabase 项目互相同步。
四、关键数据
| 指标(官方称,2022 年) | 数值 |
|---|---|
| 演示包体积 | 约 30 MB |
| 压缩后初始状态 | 约 12 MB(含已运行的 Postgres + psql) |
| Postgres 版本 | 14.5(初始状态内为 14.4) |
| 可配内存 | 128 MB – 1024 MB |
| 持久化 | 文件导出 / IndexedDB |
| 访问方式 | 浏览器内 psql / 外部 psql 经 websocket 代理 |
五、评测方法批判:它的”快”和”小”怎么读
需要把官方口径放在工程语境里看:
- 12MB 是”含已运行 Postgres 的快照”,是反复压缩调优后的结果,不是随意起一个 Postgres 的成本;首次加载仍要下载几十 MB,“刷新后秒开”是浏览器缓存了初始快照之后的体验;
- 性能官方根本没测,也没承诺——在 JS/WASM 里模拟一颗 x86 CPU 跑 Linux 再跑 Postgres,每层都有解释/翻译开销,它从设计上就不是为性能,而是为”能在浏览器里跑起来”这件事本身;
- 网络依赖外部托管代理(早期用
wss://relay.widgetry.org/,后来是 Supabase 自己的proxy.wasm.supabase.com)。也就是说,这个”浏览器里的数据库”要对外通信,离不开一个第三方的常驻服务,并不是完全自包含; - Postgres 版本停留在 2022 年的 14.5,公告本身也反复强调”very experimental”。
六、官方自己承认的边界
- “现阶段不适合通用场景”:30MB 的首包、模拟器的性能开销,都决定了它不是要替代你连接的那个真 Postgres;
- 不是纯 WASM:它是整机模拟,复杂度和体积都远高于 sql.js 这种”SQLite 编译成 WASM”的路线;
- 项目定位是实验:两个仓库(snaplet/postgres-wasm 与 supabase-community 的 fork),官方在文末说”如果想参与就联系我们”,没有给出明确的 GA/路线图承诺;
- 网络隧道是外挂:想让外部工具(PgAdmin、本地 psql)连进来,必须经过他们的代理服务,端口还按内网 IP 末两段映射。
七、适用与不适用
适合: 写带可运行 Postgres 的教程/文档与交互式 Demo;想在浏览器里离线体验/教学 SQL、触发器、逻辑复制;做”一键起一个隔离的 Postgres 测试环境”的原型;以及体验”浏览器里的数据库”这件事本身。
不适合: 把它当成生产数据库、高并发前端存储,或者期待它像 sql.js 那样轻量的人。要在浏览器里做轻量数据,SQLite 系(sql.js / absurd-sql / 前面文章提到的 CozoDB WASM、Limbo WASM)才是更合适的工具;postgres-wasm 的价值在于”让一个完整的、带 5432 线协议和逻辑复制的 Postgres,能在客户端沙箱里被打开”。