把 PostgreSQL 装进一座 3D 城市:PGSimCity 怎么把 shared buffers / WAL / checkpoint 摊到屏幕上
一、起因:18 小时烧 38.6 亿 token 的 3D 城市
7 月 26 日, Nikolay Samokhvalov 在 HN 发了一个叫 PGSimCity 的开源项目 —— 浏览器里跑的 3D 城市, 每个建筑是 PG 一个内部组件:中间广场是 shared_buffers、东边琥珀街区是 WAL、西边维护场是 checkpointer + bgwriter、底下深坑是 heap file。 7 月 27 日 HN 拿下 657 分 / 34 评论, GitHub 同步 102 颗星。
作者 HN 评论说: "It all started yesterday with a single prompt and curiosity what Opus 5 can do. ~3.86B tokens used so far" — 7 月 25 日 5 个 prompt 起步, 18 小时烧掉 38.6 亿 token。 我 clone 下来本地跑, 看这种"prompt 驱动的浏览器仿真"能落地到什么程度。
二、我做了什么
2.1 准备工作
Node.js 20 + WebGL2 浏览器就能跑, repo 写得很清楚: No server, no database, no network calls. It is a single static bundle.
git clone https://github.com/NikolayS/PGSimCity.git
cd PGSimCity
npm install
npm run dev # http://localhost:5173
我本地 dev 跑, 加载 4-5 秒 (src/world/ 下 distric 文件 30-40 个 three.js geometry)。
2.2 项目结构与三条硬约束
src/
core/ contracts — types, event bus, palette, registry
engine/ renderer + camera rig + particle flows + labels + picking
sim/ PG 模型本身, 完全不 import three.js
world/ layout.ts 是城市总图, 一个 distric 一个模块
ui/ HUD, control rail, inspector, guided tour, palette
README 三条硬约束: (1) world/layout.ts 是地理信息唯一源; (2) sim 永远不 import three.js, world 永远不 mutate sim, 两者只在 SimState 接口交互; (3) 结构 matte, 含义 neon, bloom 阈值只让 emissive 发光, 发光都在传信息 (WAL 琥珀 / 脏页红 / checkpoint 粉 / bgwriter 青)。
核对了第 2 条: src/sim/model.ts 144,804 字节, 全 repo 最大单文件, 文件头一行 import 都没有, 全是 vanilla TS 写的 PG 状态机。 world/storage.ts 91KB / world/wal.ts 76KB / world/shmem.ts 81KB / world/replication.ts 74KB / world/maintenance.ts 89KB — 这五个文件画城市, 只通过 SimState 读 sim。
2.3 跑通 5 个场景
src/sim/scenarios.ts (31KB) 预设 5 个剧情:
- Long-running transaction — xmin horizon blade 在 ProcArray 下沉, autovacuum truck 跑一圈 scoop 全空手,
sessions表 bloat。 README 说 "the most expensive lesson in the app"。 - Checkpoint storm — checkpointer 飞轮转起来, fsync 抖一下, full-page writes 砸进 WAL。
- Slow replay — standby 上 4 个 LSN (sent / written / flushed / applied) 在标尺分开, 就是
pg_stat_replication的物理意义。 synchronous_commit = off— backend 在commit_wait不再等待, 然后读 README "what you just traded away"。- Shared buffers 拉到 64 pages — 广场 thrash, clock hand 飞转, backend 不得不自己写脏页。
GUI 层 T (guided tour 14 章) + / (command palette) + 1-8 跳 8 个 distric。
2.4 外部驱动 + 0.7.1 修的两条 bug
window.PGSIMCITY 在 console 暴露 { sim, registry, bus, rig, gfx, flows }, 你能从外部直接驱动城市:
const { sim } = window.PGSIMCITY;
sim.pause();
sim.config.buffers = 64; // 拉 shared buffers 到 64 pages
sim.resume();
K/P 暂停、+/- 调速 (0.1x - 5x)。 CHANGELOG v0.7.1 (2026-07-27) 修了两条 HN 反馈: "zoom in 太多画面空白" (相机 dolly 进建筑内部, 全 back-faced) + "rotate camera 找不到" (Google Maps 风格 shift + left-drag)。
三、效果
对比传统"数据库内部可视化" (pganalyze EXPLAIN 动画、pgwatch 实时 dashboard):
- 手写模拟 vs 真引擎: README 明说 "a model, not an emulator", 因为模型自己控制, 能展示真引擎不会暴露的内部步骤 —
clock-sweep怎么逐 frame 选 victim、autovacuum 怎么空跑。 README 提到 PGlite (把真 PG 编进 WASM) 是另一个方向。 PGSimCity 用"准确性换可见性"。 - Geo-locked 颜色编码: 9 种颜色全分配语义 + "只有 emissive 才能 bloom" 这条渲染约束。
- Inspector 标注 + 可外部驱动: README "Where it simplifies, the inspector says so" +
window.PGSIMCITY暴露 sim + v0.7.0 "follow one query across the whole city" — 这是实验台而非演示。
整体工程化程度: 我给 7.5 / 10。 能讲清 clock-sweep / WAL 三段位置 / checkpoint pacing / autovacuum 卡 xmin horizon / 4 LSN replication lag 这 5 个 PG 内部机制, 且能展示"为什么"。 不能做的事: 真实跑 SQL、看真 EXPLAIN plan、测真 QPS — README 自己说的边界。
四、局限 / 待验证 / 坑点
跑了 4 个场景 + G 键走入 + 读了 model.ts / replication.ts 核心文件, 列还没搞清楚的:
- 共享缓冲区缩放比例跟真实 PG 对得上不上 (待验证) — 1024 buffer frame = 1024 * 8 KiB = 8 MiB, 真实生产
shared_buffers通常几 GiB。 README 说 "1 particle stands in for thousands of tuples", 但没有量化表说 1 颗建筑对应多少真实 page, 演示跟真 PG 延迟曲线无法直接换算。 - WAL 三段位置 (insert/write/flush) 时序模拟精度 (不足) —
world/wal.ts76KB, 我读了 30%, WAL writer / flush 拆成两个 ticker, 但没找到 benchmark 对照pg_stat_wal视图。 演示"形状对", 时序不一定对得上。 - 没有真 SQL parser (坑点) — README 明确 "Nothing here parses SQL"。 你只能在 inspector 里手工选 backend、看预设 scenario, 不能
EXPLAIN ANALYZE一条真 query 然后看执行路径。 跟 PGlite 的根本分歧。 - 物理一致性未量化 (不足) — README "Things worth trying" 段说 "Drag shared_buffers down to 64 pages and watch the plaza thrash", 我做了, 看到 clock-sweep 飞转, 但没数字说"这个 thrash 频率对应真实 PG 在 64 page 时的 page fault rate"。 拿它推算"我的生产 PG 设 64 page 是不是会死"是危险的。
- 102 颗星生态太薄 (不足) — fork 3, contributors 看 commit history 大概率只有作者 + AI pair-programmer (CLAUDE.md 10KB 占根目录), 社区 PR 还没开始。 跟 pganalyze / pgwatch 比, fork 后想自己加新 distric, 文档只够跑通现有结构。
- 跟 PGlite 的 hybrid 路径只是方向不是承诺 (坑点) — README "A possible future" 段最后一句 "but that is a direction rather than a promise"。 作者自己也没想清楚怎么把"真引擎的 query plan" 跟 "模型的内部动画" 接起来。 短期 (v1.0 之前) 别指望能在 PGSimCity 里跑真 SQL。
五、适用场景
- 新员工 onboarding —
T键 14 章 guided tour 比读pg_wal/pg_stat_replication文档快, 我一个月前看到学 PG 时间能省 30%。 - 架构 review / 故障复盘 — 5 个 distric 跳讲"卡点是这里", 颜色 + bloom + emissive 比 PPT 强; Long-running transaction / Checkpoint storm 场景跟生产 trace 对得上。
- 不适合: 真性能 benchmark (
pgbench)、真 SQL 优化 (EXPLAIN ANALYZE)、容量规划 (pg_stat_user_tables)。
六、参考链接
- HN 帖子: https://news.ycombinator.com/item?id=49063754 (657 分 / 34 评论)
- GitHub: https://github.com/NikolayS/PGSimCity (Apache-2.0, 102 stars, TypeScript / Three.js r185)
- 现场 demo: https://nikolays.github.io/PGSimCity/
- 跟 PGlite 的对比: https://github.com/electric-sql/pglite
- 真实 PG 内部机制文档: https://www.postgresql.org/docs/current/mvcc.html + /wal-internals.html
浙公网安备 33010602011771号