生产迁移的核心是让数据库变更可审查、可追踪,并能配合应用发布。Go 方案主要分为版本化迁移(提交并按序执行迁移文件)和声明式迁移(根据目标 Schema 生成差异)。生产环境通常应执行已审核的版本化迁移。
先给结论
| 场景 | 推荐 | 选择理由 |
|---|---|---|
| 手写 SQL,追求简单透明 | golang-migrate | SQL 文件、CLI 和 Go API 都直接 |
| 需要 SQL 和 Go 函数迁移 | goose | 支持 embed.FS,可用 Go 处理复杂回填 |
| 自动生成迁移并做 CI 风险检查 | Atlas | 支持 Schema diff、版本化迁移和 lint |
| 原型、测试库或可重建数据库 | GORM AutoMigrate / Ent Automatic Migration | 无需维护完整迁移历史 |
如果没有 ORM 约束,SQL 项目从 golang-migrate 开始;需要 Go 代码参与迁移时选 goose;需要 Schema diff 和 CI 检查时再引入 Atlas。
版本化和声明式迁移
版本化迁移把每次变更保存为有序文件。文件进入生产后应视为历史记录;要修复问题,就新增迁移。它容易审查,但需要开发者明确处理改名、数据回填和索引构建。
声明式工具根据当前和目标 Schema 生成差异,能减少重复 DDL。生成结果仍需审查:工具未必知道字段改名要保留数据,也无法替你决定如何分阶段发布。稳妥做法是生成版本化 SQL,提交到 Git 后再执行。
常见方案
golang-migrate
SQL-first 的版本化迁移工具,提供 CLI 和 Go 库,可与 database/sql、sqlx、sqlc 等配合。
- 适合:希望迁移 SQL 清晰、与 ORM 解耦的项目。
- 注意:失败可能留下 dirty 状态,修正前先核对数据库实际状态。多实例不要同时启动迁移;用独立 Job,或明确配置并验证锁行为。
down只能执行反向 SQL,不能恢复已删除的数据。
goose
支持 SQL 文件和 Go migration,也支持将迁移嵌入二进制。
- 适合:需要批量回填、业务规则转换,或希望单独发布 migration binary 的项目。
- 注意:能用 SQL 表达的变更通常继续用 SQL;多实例执行时要配置锁或使用单独 Job。迁移事务行为还要按数据库和语句确认。
Atlas
支持从 Schema 或 ORM 模型生成迁移,并对迁移做 lint。可用于 GORM、Ent 等项目,也能与现有执行器组合。
- 适合:Schema 较复杂,或希望在 CI 中检查危险 DDL 的团队。
- 注意:自动 diff 不理解业务语义。字段改名可能被生成为删列再建列;生成 SQL、锁表影响和数据保留策略仍需人工审查。生产优先使用提交审核过的版本化迁移。
GORM AutoMigrate 和 Ent Automatic Migration
两者都能按模型创建或调整 Schema,适合本地开发和可随时重建的测试库。GORM AutoMigrate 不会自动删除旧列;Ent Automatic Migration 默认偏向追加变更。Ent 也支持通过 Atlas 生成版本化迁移。
不建议把运行时自动对齐作为重要生产数据库的默认流程:它缺少清晰的变更审查与发布边界,大表 DDL 也可能锁表或耗时。
能力对比
| 方案 | 迁移输入 | 自动生成 SQL | CI 风险检查 | 生产建议 |
|---|---|---|---|---|
| golang-migrate | SQL | 否 | 需自行组合 | 适合 |
| goose | SQL / Go | 否 | 需自行组合 | 适合 |
| Atlas | Schema / ORM | 是 | 内置 lint | 用版本化工作流 |
| GORM / Ent 自动迁移 | ORM 模型 | 是 | 需自行组合 | 仅限开发或可重建库 |
生产实践
- 单独执行迁移:由 CI/CD 或一次性 Job 运行,不让每个应用实例启动时抢跑。
- 向前修复:迁移进入生产后不要改写;反向 DDL 不等于数据恢复。
- 分阶段发布:字段改名、大表变更采用“新增兼容结构、回填、切换应用、清理旧结构”的步骤,保证新旧应用可短暂共存。
- 在目标数据库上检查:CI 至少从空库执行迁移,并尽量使用与生产相同的大版本;SQLite 通过不代表 PostgreSQL 或 MySQL 的锁和 DDL 行为相同。
参考资料
相关主题:项目选型。