团队里 3 个服务一个用 Go、一个用 Node、一个用 Python,数据库都是 Postgres。每个服务各自带一套框架内置的迁移(Rails / DRF / Sequelize / GORM),同一个 schema 变更要改 3 套不同语法的迁移文件,migrate 命令还各不一样。多人开发 + 生产部署时,“谁先跑了哪个迁移” 就成了一笔糊涂账。
dbmate 是给这种场景准备的解法:一个独立二进制、纯 SQL 迁移文件、不绑任何语言或框架,Postgres / MySQL / SQLite / ClickHouse 共用同一套迁移,跑起来谁先谁后它自己记账。
TL;DR
- dbmate 是 Go 写的数据库迁移工具,单二进制、零运行时依赖,不绑编程语言和框架。
- 迁移文件就是纯 SQL,按时间戳命名(如
20260925120000_create_users.up.sql),Git 友好、天然可排序。- 支持 PostgreSQL、MySQL、MariaDB、SQLite、ClickHouse,乃至 BigQuery、Spanner。
- 自带
schema_migrations表记账 +schema.sql全量 dump,多环境对账清晰。- 适合"多语言服务共享一个库"或"不想被某框架绑死迁移"的场景;单一框架统一管着的团队未必需要。
它和框架内置迁移差在哪?
先把定位说清。Rails / GORM / Django 的迁移是跟着框架走的:你写 Go 就 GORM 的 AutoMigrate / Migrator,写 Python 就 Django 的 migration 文件。优点是生态顺,缺点是语言绑定——换个语言栈迁移方式全变。
dbmate 反过来:语言无关。它只认 SQL。不管你的服务是 Go、Node、Python 还是 PHP,迁移都写同一套 .sql 文件,用同一个二进制跑。
| 维度 | dbmate | 框架内置迁移(GORM/Django/Sequelize) |
|---|---|---|
| 语言绑定 | 无(纯 SQL) | 有(跟框架走) |
| 依赖 | 单 Go 二进制 | 依赖框架 + 运行时 |
| 跨库 | 同一套 SQL 多库 | 多数框架单库/方言差异大 |
| 状态记账 | schema_migrations 表自管 |
各框架各表 |
| schema dump | 内置生成 schema.sql |
多数要另配 |
| 适用 | 多语言混用 / 自架党 | 单一框架团队 |
为什么"纯 SQL + 时间戳"是个关键设计
多数迁移工具要么生成难读的语言对象(GORM 的 *gorm.DB 结构体),要么把 SQL 埋在框架里。dbmate 选择把迁移本身当数据:
# 一个迁移就两个文件
migrations/20260925120000_create_orders.up.sql
CREATE TABLE orders (id BIGSERIAL PRIMARY KEY, ...);
migrations/20260925120000_create_orders.down.sql
DROP TABLE orders;
- 时间戳前缀保证全团队排序一致,不会出现"1、2、3"这种本地相对编号
.up/.down成对,回滚就是跑.down- 纯 SQL 意味着DBA 不用看你代码就能 review 这次 schema 变更,门槛低
它怎么保证多环境不跑偏
两个内置机制让"多个开发者 + 生产"对账清晰:
schema_migrations表:记录每个已跑迁移的版本,启动时自动补齐漏跑的、跳过跑过的。多人各跑各的,合并后跑一次就收敛。schema.sql全量 dump:每次 run 后导出一份当前 schema 快照。新环境或 CI 直接psql -f schema.sql建库,不用从 0 重放所有迁移——这是团队协作里最省时的一个点。
自架党 / 多服务场景,它值不值
适合:
- 多个微服务共享一个库,语言栈混着(Go + Node + Python)
- 自架党要一个不绑框架、好审计的 schema 管理工具
- 生产迁移想要"幂等 + 可回滚 + 有全量快照"这套标准动作
- 同时管 Postgres 和 ClickHouse(少见但 dbmate 真支持)
不太适合:
- 整个团队就一个 Django / Rails 应用,框架迁移够用,再引一个 dbmate 是叠床架屋
- 需要 ORM 层自动生成迁移(如 GORM AutoMigrate 从结构体生成 DDL)——dbmate 不碰 ORM,迁移得手写 SQL
常见问题
Q1: 不写代码、纯 SQL 文件,怎么回滚?
A: 每个迁移有 .up.sql 和 .down.sql 成对文件,dbmate --drop-database 或反向跑 .down 即可。手写 .down 的负担在你,但换来 SQL 的可读和可审计。
Q2: 它能建库 / 删库吗?
A: 能。支持 create-database / drop-database 命令,多环境一键起停。
Q3: 多个服务同时跑 dbmate 会冲突吗?
A: 靠 schema_migrations 表的版本记录收敛。但同一张表被两个服务各写各的迁移,设计上就该归一个服务管——工具解决"跑的顺序",不解决"谁该拥有这张表"。
Q4: 生产迁移中途挂了怎么办?
A: 纯 SQL 默认事务内执行,失败即回滚该迁移,schema_migrations 不记脏版本。大表 DDL(加列 / 索引)建议拆小 + 在线 schema 变更工具配合。
Q5: 和 goose / golang-migrate 比?
A: 都是纯 SQL 路线。dbmate 多支持几个库(ClickHouse / BigQuery / Spanner)、内置 schema dump、能建删库。单一 PG 场景里差异不大,看 CLI 习惯。
Q6: 安装麻烦吗?
A: 单二进制。brew install dbmate(macOS)、scoop install dbmate(Windows)或直接从 GitHub Releases 下对应平台二进制丢进 PATH,dbmate --version 验一下就行。