go-zero 需要数据库迁移工具吗?怎么做

「go-zero 是不是不用迁移工具?一般怎么做?类比 Python 的 alembic 又是啥样?」—— 这篇讲清 go-zero 的迁移哲学、主流做法,以及与 Python alembic 的对照。

一句话结论 为什么没内置 一般怎么做 golang-migrate Python alembic 分工流程 踩坑 框架对比 结论

go-zero 不内置迁移工具,但生产项目照样要用

go-zero 的命令行 goctl 和框架本身都不带数据库迁移(migration)能力。但这不等于「不用迁移」——真实项目表结构会变,迁移照样要做,只是用外部工具(golang-migrate / goose / flyway)或手写 SQL,框架不替你管表结构。

记得住的一句话:go-zero 把「建表权」交还给你(或 DBA),不像 GORM 那样 AutoMigrate 偷偷改表、也不像 Python 那样有框架内建的 alembic。迁移是独立的一环,和 goctl model 生成代码是前后两步、各管各的

哲学:表结构由你掌控,不靠框架偷改表

这跟「go-zero 建表与 Model」篇里说的「goctl 不执行 CREATE TABLE」是一脉相承的。

go-zero 的设计取舍
生产环境表结构是核心资产,应该显式、可追溯、可回滚。框架自动改表(如 AutoMigrate)在复杂变更下不可控,go-zero 干脆把这块让出来。
goctl 的角色边界
goctl model 是「读表生成代码」,不是「改表」。它假设表已经存在且稳定,只负责把表映射成 Go Model。
别用启动即执行 DDL 的野路子:有人图省事在 svc.NewServiceContextconn.Exec("CREATE TABLE IF NOT EXISTS ..."),小规模 demo 能跑,但没有版本号、无法回滚、多实例并发建表有竞态,生产千万别这么干。

四种主流做法,按项目规模选

方案适合做法 A. 手写 SQL小项目 / 个人自己写 *.sql,在 MySQL 客户端或 CI 里执行,最直白 B. golang-migrateGo 项目主流带版本号的 up/down 迁移文件 + CLI,可嵌进程序启动跑 C. goose轻量、Go 原生SQL 或 Go 写迁移,单二进制、零依赖,支持 embed D. flyway / dbmate跨语言团队数据库无关、CLI 驱动,和具体语言解耦
绝大多数 Go / go-zero 项目选 B(golang-migrate)——它语言无关、有版本表、能 up/down、还能 embed 进 Go 二进制在部署时自动执行。下面以它为例。

golang-migrate 一条龙

装工具 → 写迁移文件 → 执行 up。这就是 go-zero 项目里「建表」的标准姿势。

Step 1 · 安装 CLI

go install -tags mysql github.com/golang-migrate/migrate/v4/cmd/migrate@latest

Step 2 · 写迁移文件(up 建表 / down 回滚)

migrations/ ├── 000001_create_user.up.sql # 正向:建表 └── 000001_create_user.down.sql # 反向:删表(回滚用)
-- 000001_create_user.up.sql
CREATE TABLE user (
  id          BIGINT NOT NULL AUTO_INCREMENT,
  name        VARCHAR(64) NOT NULL DEFAULT '',
  create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 000001_create_user.down.sql
DROP TABLE user;

Step 3 · 执行迁移

migrate -path ./migrations \
        -database "mysql://root:password@tcp(127.0.0.1:3306)/test" \
        up

进阶 · 嵌进 Go 程序,启动时自动跑(可选)

m, _ := migrate.New("file://migrations",
    "mysql://root:password@tcp(127.0.0.1:3306)/test")
m.Up()  // 部署时执行,跑过的有版本记录,不会重复
迁移执行后,schema_migrations 表里会记录已跑到哪个版本号——这正是「迁移工具」和「随手执行 SQL」的本质区别:有版本、可追溯、可回滚

Python 的 alembic 是怎么干的

SQLAlchemy 生态用 alembic 做迁移,工作流和 golang-migrate 神似,但多了一个「自动生成」能力。

alembic 标准流程

# 1. 初始化(生成 alembic.ini / env.py / versions/)
alembic init migrations

# 2. 改了 Model 后,自动比对生成迁移脚本
alembic revision -m "add user table" --autogenerate

# 3. 执行迁移(按版本号顺序 up)
alembic upgrade head

# 4. 回滚一个版本
alembic downgrade -1
migrations/ ├── env.py # 读 sqlalchemy.url,连接库 ├── script.py.mako # 迁移模板 └── versions/ └── 3a1b_xxx_add_user_table.py # upgrade()/downgrade()
和 golang-migrate 相同
版本号管理、up/down 双向、versions 目录、不重复执行、可回滚。
关键不同
alembic 有 --autogenerate,能比对 ORM Model 与库结构自动生成迁移;golang-migrate 的 SQL 迁移全手写(go 社区没有等价 ORM 自动比对)。
autogenerate 不是万能的:它检测不到列重命名(会当成「删旧+加新」)、检测不到某些类型变更、检测不到数据迁移。真要稳,生成的脚本也得人肉 review。这点 Python 和 Go 的「手写 SQL 派」共识一致——迁移脚本要审

迁移工具与 goctl model 是前后两步

迁移管「表结构」,goctl 管「代码」。两者配合,不重叠。

① 迁移工具 golang-migrate up ② 表落到库 DB 有 user 表 ③ goctl model 读表生成代码 svc→logic
正确顺序:先迁移建表 → 再 goctl model 生成 Model。改表时也先加迁移文件执行、再重跑 goctl 重新生成 Model。别反过来。

迁移最容易翻车的几点

坑说明正确做法 用框架 AutoMigrate 上生产GORM/SQLAlchemy 自动改表对复杂变更不可控生产用带版本号的迁移工具 迁移文件不进版本控制别人拉不到、环境不一致migrations/ 必须提交 git 忘了写 down出问题无法回滚每个 up 配套 down(哪怕 DROP) 直接手改线上表又跑迁移版本号错乱、迁移失败所有变更走迁移,禁手动改 改表后没重生成 Model代码还是旧字段迁移后重跑 goctl model 大表加列不加 ALGORITHM锁表拖垮线上MySQL 8 用 INSTANT / 低峰执行

各框架「建表」机制一览

框架 / 工具内置迁移?建表方式 go-zero❌ 无外部迁移工具(golang-migrate 等)+ 手写 SQL;goctl 只生成代码 GORM⚠️ 半自动AutoMigrate 自动改表(不推荐上生产管复杂变更) Python alembic✅ 生态内建revision --autogenerate + upgrade head,可自动比对 Model Django ORM✅ 内建makemigrations + migrate,最像 alembic golang-migrate✅ 独立工具版本化 up/down SQL,语言无关,可嵌 Go
一句话总结生态差异:Python 圈把迁移做成框架/ORM 内建能力(alembic/Django)Go 圈倾向把迁移当独立工具(golang-migrate/goose),框架本身不管——go-zero 正是后者典型。

记住这点就够

go-zero 不用「框架内建迁移」,但生产项目要用「独立迁移工具」。最小可用是手写 SQL,主流是 golang-migrate(版本化 up/down),类比 Python 的 alembic(多一个 autogenerate)。迁移负责「把表建进库」,goctl model 负责「读表生成代码」——前者在先,后者在后,分工明确

go-zero 立场
不内置迁移,表结构由你掌控,框架不偷改表。
Go 一般做法
golang-migrate(SQL 全手写)/ goose,版本化、可回滚。
Python 对照
alembic 工作流一致,但能 --autogenerate 自动比对 Model。
协作顺序
迁移 up 建表 → goctl model 生成 → 改表先迁移再重生成 Model。
和「go-zero 建表与 Model」篇连读:那篇讲 goctl 怎么生成 Model,这篇讲表是怎么来的。两篇拼起来就是完整的「表 ↔ 代码」链路。