Apache License 2.0

技术解读

以下内容由 GitRead 中文社区 编辑团队基于项目 README 与官方资料整理, 结合中文开发者的常见使用场景补充技术选型建议。具体以项目官方文档为准。

DBLab Engine 深度解析:把 Postgres 变成能秒级分支的数据仓库

项目背景与痛点

开发团队的日常大概是这样:为了验证一行 schema 变更,从生产环境拉一份数据,pg_dump 跑三小时,导入又两小时,磁盘直接被占满。CI 里跑集成测试,要么拿 SQLite 假装成 Postgres,要么所有人共用一个"测试库",跑一次被别人的脏数据污染一次。DBA 想验证一个慢查询的优化方案,只能对着一个随时被写坏的库,或者等凌晨的低峰窗口。

问题的本质只有一句话:数据库副本太贵。数据量一旦上到 TB 级,复制一份的时间成本和存储成本都是线性的,而绝大多数开发、测试、SQL 调优场景,其实只需要一个只读的、用完即弃的数据视图。

DBLab 的切入角度很直接——不复制数据,只复制指针。它借用文件系统的写时复制能力,把一个巨大的 Postgres 数据目录变成"秒级可分支"的对象,操作语义上接近 Git branch。

核心技术揭秘

DBLab 把系统拆成三层:数据同步、存储、实例编排。

同步层负责从生产库拿到一份"足够新"的数据。它支持两条路径:pg_basebackup 全量拉取,以及通过 WAL 流持续追日志、把增量落进快照池。这里有个值得玩味的设计取舍——DBLab 并不追求实时一致性,而是按 schedule 定期刷新。这个选择很聪明:CI 测试和 SQL 调优对数据新鲜度的要求通常是小时级,硬要做秒级实时,只会把复杂度和对生产库的压力一起抬上去。

存储层是整个系统的地基。ZFS 或 LVM 提供 CoW 快照,克隆出的数据集在创建瞬间不占任何额外空间,只有在被写入时才分配新块——这就是 “thin cloning” 的字面含义。克隆实例启动后读到的仍是原快照的块,所以一个 3TB 的库能在一秒内克隆出十个实例,磁盘开销只有几十 MB。

编排层用容器跑起真正独立的 Postgres 进程,每个 clone 有自己的端口、内存配额和 TTL,过期自动回收。之所以必须起真题进程而不是共享一个,是因为要验证的是"真实的 Postgres 行为"——扩展、版本、参数、锁语义,都得原汁原味。

最容易被忽视、也最难的一块是数据预热。快照刚挂载时,操作系统 page cache 和 shared buffers 全是冷的,新克隆实例第一次跑查询会慢得离谱,基准测试出来的数字完全失真。DBLab 为此做了预加载:依据源库的实际数据分布(哪些块真被访问过)或借助 pg_prewarm,在克隆启动后主动把热数据读进缓存。这一步做不好,所谓"秒级启动"就变成了"秒级启动、分钟级预热"的假象。

graph TD A[生产 Postgres] -->|"WAL 流 或 pg_basebackup"| B[同步引擎] B --> C[存储层(ZFS 或 LVM)] C --> D[快照池 Snapshot Pool] D --> E{"克隆场景"} E -->|"CI 集成测试"| F[CoW thin clone A] E -->|"迁移演练"| G[CoW thin clone B] E -->|"SQL 调优"| H[CoW thin clone C] F --> I[数据预热] G --> I H --> I I --> J[实例对外提供服务]

功能亮点与差异

竞品大致分三类:Neon、Supabase Branching 这类自带存储层的 serverless Postgres,分支能力内建,但要求你把数据搬过去,且通常不兼容任意扩展;Docker 加快照加脚本的自研方案,能跑,但没人长期维护;Delphix 这类企业级产品,贵,而且不开源。

DBLab 的差异点集中在三处:

其一,它不挑数据库。 不管版本、不管装了什么扩展、不管数据是从 RDS 导出的还是自建的。它只认数据目录和文件系统,不侵入数据库内部,因此迁移成本几乎为零。这一点在真实环境里比任何功能清单都重要。

其二,自托管、Apache 2.0。 数据不出你家的 VPC。对金融、医疗这些合规敏感的场景,这是硬需求,而不是加分项。

其三,它把"克隆"做成了一个带 API 的服务,而不是一个脚本。 CI pipeline 里一次调用就拿到一个干净的全新库,测试跑完自动销毁。这个抽象层次,决定了它真的能被 GitLab CI、GitHub Actions 接进去,而不是停在 README 的示例里。

还有一个常被忽略的收益:因为所有克隆共享同一份快照池数据,脱敏只需要在同步环节做一次,不必每个实例各跑一遍。

应用场景与落地建议

最契合的场景是中大型 Postgres 团队(数据量几百 GB 到几十 TB),CI 里有大量依赖真实数据的集成测试,或者频繁做 schema 迁移与大版本升级演练。

落地前有几件事必须先想清楚:

  • 存储要单独规划。 ZFS 需要直连磁盘或独占块设备,云上用 EBS 挂 ZFS 效果打折明显,IOPS 会迅速成为瓶颈。至少给它一块独立的高性能卷,别和生产库抢同一份 IO。
  • 预热策略要自己调。 默认预热未必覆盖你的关键查询路径,建议结合慢查询日志定制,否则测出来的性能数字不可信。
  • 克隆实例的内存配额必须设死。 CoW 只省磁盘不省内存,十个克隆各开 2GB shared_buffers,宿主机立刻被拖垮。TTL 和资源上限是必配项。
  • 别指望它替代高可用或备份方案。 它解决的是"数据副本"成本问题,不是故障恢复问题,生产备份该怎么做还怎么做。

综合评价

优点:思路干净,用文件系统的能力去解数据库层的成本题,四两拨千斤;对 Postgres 生态零侵入,兼容性极好;自托管加开源,在合规敏感场景里属于稀缺品;把克隆抽象成 API,CI 集成体验做得相当到位。

缺点也得说:强依赖 ZFS/LVM,这个前置条件已经劝退了一批只想 docker run 的尝鲜者;Go 代码库规模不小,二次开发和运维需要团队里有人真懂存储;数据同步非实时,想"看最新生产数据"的场景不适用;预热没调好,性能数据会直接误导决策——这是最容易踩的坑。

一句话:如果你的团队正在为"每个测试环境都要一份全量数据库"持续付账单,DBLab 是当前开源世界里最接近正解的那个答案。它不是银弹,但它的架构选择足够诚实。

关于

GitHub 原始描述(英文)

DBLab enables 🖖 database branching and ⚡️ thin cloning for any Postgres database and empowers DB testing in CI/CD. This optimizes database-related costs while improving time-to-market and software quality. Follow to stay updated.

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

语言占比

CSS
Dockerfile
Go
HTML
JavaScript
Makefile
SCSS
Shell
TypeScript

Release

README


DBLab Engine

⚡ Blazing-fast PostgreSQL cloning and branching 🐘

🛠️ Build powerful dev/test environments.
🔃 Cover 100% of DB migrations with CI tests.
💡 Quickly verify ChatGPT ideas to get rid of hallucinations.

Available for any PostgreSQL, including self-managed and managed services* like AWS RDS, GCP Cloud SQL, Supabase, and Timescale.

It can be installed and used anywhere: across all cloud environments and on-premises.

Latest release

CI pipeline status Go report

Contributor Covenant Community Slack Twitter Follow


*For managed PostgreSQL cloud services like AWS RDS or Heroku, direct physical connection and PGDATA access aren’t possible. In these cases, DBLab should run on a separate VM within the same region. It will routinely auto-refresh its data, effectively acting as a database-as-a-service solution. This setup then offers thin database branching ideal for development and testing.

Why DBLab?

  • Build dev/QA/staging environments using full-scale, production-like databases.
  • Provide temporary full-size database clones for SQL query analysis and optimization (see also: SQL optimization chatbot Joe).
  • Automatically test database changes in CI/CD pipelines, minimizing risks of production incidents.
  • Rapidly validate ChatGPT or other LLM concepts, check for hallucinations, and iterate towards effective solutions.

For example, cloning a 1 TiB PostgreSQL database takes just about 10 seconds. On a single machine, you can have dozens of independent clones running simultaneously, supporting extensive development and testing activities without any added hardware costs.

Try it yourself right now:

How it works

Thin cloning is fast because it is based on Copy-on-Write (CoW). DBLab employs two technologies for enabling thin cloning: ZFS (default) and LVM.

Using ZFS, DBLab routinely takes new snapshots of the data directory, managing a collection of them and removing old or unused ones. When requesting a fresh clone, users have the option to select their preferred snapshot.

Read more:

Where to start

Case studies

Features

  • Speed & scale
    • Blazing-fast cloning of PostgreSQL databases – clone in seconds, irrespective of database size
    • Theoretical max of snapshots/clones: 264 (ZFS, default)
    • Maximum size of PostgreSQL data directory: 256 quadrillion zebibytes, or 2128 bytes (ZFS, default)
  • Support & technologies
    • Supported PostgreSQL versions: 10–18
    • Thin cloning (CoW) technologies: ZFS and LVM
    • UI for manual tasks and API & CLI for automation
    • Packaged in Docker containers for all components
  • PostgreSQL containers
    • Popular extensions including contrib modules, pgvector, HypoPG and many others (docs)
    • Customization capabilities for containers (docs)
    • Docker container and PostgreSQL configuration parameters in the DBLab config
  • Source database requirements
    • Location flexibility: self-managed PostgreSQL, AWS RDS, GCP Cloud SQL, Azure, etc.—no source adjustments needed.
    • No ZFS or Docker requirements for source databases
  • Data provisioning & retrieval
    • Simple-mode setup: paste source URL + password, get a probed configuration in one click (UI Configuration → Simple tab)
    • Expert-mode form for both logical (dump/restore) and physical (WAL-G, pgBackRest) retrieval
    • Physical (pg_basebackup, WAL-G, pgBackRest) and logical (dump/restore) provisioning
    • Partial data retrieval in logical mode (specific databases/tables)
    • Database renaming during snapshot creation (databaseRename option)
    • Continuous update in physical mode
    • Periodic full refresh in logical mode without downtime
  • Recovery & management
    • Fast Point in Time Recovery (PITR) for physical mode
    • Auto-deletion of unused clones
    • Time-based auto-deletion of unused branches and snapshots (retention config; safe-only, never force-deletes dependents)
    • Snapshot retention policies in DBLab configuration
  • Clones, branches & snapshots
    • “Deletion protection” for clones, branches, and snapshots (blocks manual and auto-deletion, plus count-based retention for snapshots)
    • Persistent clones withstand DBLab restarts
    • “Reset” command for data version switching
    • “Upgrade” command to move a single clone to a newer PostgreSQL major version in place
    • Resource quotas: CPU, RAM
  • Monitoring & security
    • /healthz API endpoint (no auth), extended /status endpoint (API docs)
    • Prometheus metrics endpoint (/metrics) for monitoring
    • Netdata module for insights

Clone major upgrade

A single clone can be moved to a newer PostgreSQL major version without touching the rest of the instance, which makes it practical to test an upgrade against production-like data:

dblab clone upgrade my-clone

The target version is not a parameter: it follows from the upgrade image the instance is configured with, which is built on that major’s binaries. The engine reports it in GET /status as cloneUpgrade.targetVersion, the CLI prints it, and the UI shows it on the confirmation dialog.

The same action is available in the UI on the clone page and over the API as POST /clone/{id}/upgrade.

The clone’s Postgres is shut down cleanly, pg_upgrade --link converts its data directory in place, and the clone restarts on an image of the target major. Because --link hard-links the data files instead of copying them, the upgrade is fast and consumes almost no extra space. The request returns as soon as the clone enters the UPGRADING state; watch the clone status for the result.

Enabling it. Set provision.pgUpgradeImage to an upgrade image of the major you want clones upgraded to. That image is the single source of the target: the engine reads the major out of a release tag (:17, :17-0.8.0, :17-0.8.0-glibc236), and re-reads it from the image’s own PG_MAJOR before every upgrade, so a tag repointed at another build is caught before any data moves. A tag outside that grammar — a digest pin, latest, a CI build — states no major, so the engine fetches the image in the background at startup and reads it from there; until that lands the upgrade reports itself unavailable. When pgUpgradeImage is unset the feature is simply unavailable and the endpoint says so; nothing else about the instance changes. The UI keeps the “Upgrade clone” button visible but disabled in that state, and its tooltip carries the reason reported on /status, so an unset image is one hover away from being diagnosed.

provision:
  pgUpgradeImage: "postgresai/pg-upgrade:17"
  pgUpgradeTimeout: 3h
  pgUpgradePullTimeout: 1h

pgUpgradeTimeout bounds a single upgrade run; it defaults to three hours. When the budget runs out, the upgrade container is removed and the clone is put back on the version its data directory still holds, exactly as it would be after any other failure.

pgUpgradePullTimeout bounds each of the two image pulls that precede it, and defaults to one hour. The upgrade image carries the target major plus the server packages of the four preceding ones, so the first upgrade on an instance downloads several gigabytes; pre-pulling both images makes the step a no-op. Both pulls happen while the clone is still serving traffic, so exceeding the budget leaves the clone untouched and running. The two budgets are separate on purpose — a slow pull must not eat into the time pg_upgrade gets once the clone is already stopped.

An upgrade request may name an image explicitly instead of letting the engine substitute the major in the current tag. provision.upgradeImageAllowList says which repositories such a request may name. When it is empty, as it is by default, only the repository of the clone image is accepted: the one databaseContainer.dockerImage points at, plus the repository of the clone’s own image when the clone has already been upgraded to another one. That default accepts exactly the image the engine would derive itself. List repositories to allow other ones, or "*" to allow any repository the instance can reach.

provision:
  upgradeImageAllowList:
    - "postgresai/extended-postgres"

Releases up to v4.2.0 accepted any repository when the list was empty. An instance that relied on that must now set the list to ["*"].

How it can end. Almost every outcome leaves the clone running:

Status Meaning
OK The clone runs the target version; dbVersion reports it.
WARNING The upgrade was not applied. Either nothing had been converted and the clone still runs its original version, or conversion had begun and the clone was rebuilt from its origin snapshot — in which case data written since the clone was created is lost. The message says which, and points at the pg_upgrade log under <clone dir>/upgrade/logs/. When the postmaster that pg_upgrade started failed, its own fatal error, with the DETAIL and HINT lines that follow it, is appended to the cause, and the run’s pg_upgrade_output.d/<timestamp>/ directory (server log, internal log, schema dumps) is preserved under the same logs/ directory; the five most recent failed runs are kept.
WARNING, clone not running The upgrade settled on disk but the clone container did not come back up. The data directory is intact and the upgrade is still recorded as pending, so the next engine start finishes or undoes it and brings the clone back.
FATAL Recovery itself failed and there is nothing left to finish. Reset or delete the clone.

Rolling back. Resetting the clone returns it to the version the instance is configured with, because a reset re-provisions from the origin snapshot using the engine-wide image.

Things to know.

  • The target must be newer than the clone’s current major and at most four majors ahead — the upgrade image carries binaries for the four preceding versions. A clone outside that range is refused; upgrading it means pointing pgUpgradeImage at a different major.
  • Only majors DBLab ships a default configuration for can be targeted (10–18 today).
  • The image tag keeps its extension bundle and glibc suffix; only the major changes (…:16-0.8.0-glibc236 → …:17-0.8.0-glibc236). Changing the glibc build across an upgrade would change collation behaviour, and pg_upgrade does not reindex. Pass --docker-image to override the choice; when its tag names a major, that major has to be the one being upgraded to.
  • pg_upgrade verifies that every extension in the source database has a matching library for the new major. An extension outside the image’s set fails this check, and the clone rolls back untouched.
  • Clones using non-default tablespaces are refused: those live outside the clone dataset.
  • In physical mode the sync instance and the pool are untouched — the upgrade only ever acts on a clone’s own dataset. The instance-wide dockerImage must still match the source major.
  • Iteration 1 limitation: a snapshot created from an upgraded clone does not record its major version, so clones created from that snapshot start on the instance-wide image and will fail to start. Avoid snapshotting upgraded clones until snapshot-level version resolution lands.

How to contribute

Support us on GitHub/GitLab

The simplest way to show your support is by giving us a star on GitHub or GitLab! ⭐

Add a star

Spread the word

  • Tweet about DBLab and mention @Database_Lab.
  • Share a link to this repository on your favorite social media platform.

Share your experience

If DBLab has been a vital tool for you, tell the world about your journey. Use the logo from the ./assets folder for a visual touch. Whether it’s in documents, presentations, applications, or on your website, let everyone know you trust and use DBLab.

HTML snippet for lighter backgrounds:

<a href="http://databaselab.io">
  <img width="400" src="https://postgres.ai/assets/powered-by-dle-for-light-background.svg" />
</a>

For darker backgrounds:

<a href="http://databaselab.io">
  <img width="400" src="https://postgres.ai/assets/powered-by-dle-for-dark-background.svg" />
</a>

Propose an idea or report a bug

For proposals, bug reports, and participation in development, see our Contributing Guide.

Reference guides

How-to guides

You can find more in the “How-to guides” section of the documentation.

Miscellaneous

License

The DBLab source code is licensed under the OSI-approved open source license Apache 2.0.

Reach out to the PostgresAI team if you want a trial or commercial license that does not contain the GPL clauses: Contact page.

Community & Support

Contributor Covenant

Many thanks to our amazing contributors!

Translations

Making DBLab more accessible to engineers around the globe is a great help for the project. Check details in the translation section of contributing guide.

This README is available in the following translations:

👉 How to make a translation contribution

评论

暂无评论