数据库迁移
数据库迁移
go-web 使用 Goose 进行版本化 SQL 迁移。框架在两处使用 Goose:
- 运行时自动迁移 —— 服务启动时,
migration.MigrationServer 会自动执行goose.Up,把所有未应用的迁移跑完 - CLI 工具 ——
cmd/migrate/main.go提供独立的命令行,可以手动up/down/status/create/redo
配置
database:
driver: "mysql" # 支持 mysql / postgresql / sqlite,memory 不支持迁移
migration:
dir: "migrations" # 迁移文件目录,相对工作目录环境变量覆盖:DATABASE_MIGRATION_DIR=path/to/migrations。
CLI 用法
# 创建新迁移文件
go run cmd/migrate/main.go create create_users_table
# 应用所有未执行的迁移
go run cmd/migrate/main.go up
# 回滚最近一次迁移
go run cmd/migrate/main.go down
# 查看迁移状态
go run cmd/migrate/main.go status
# 回滚最近一次然后重新执行
go run cmd/migrate/main.go redoCLI 内部会执行最小装配链(Env → Config → Logger → Database),拿到 *gorm.DB 后转成 *sql.DB 交给 Goose。
迁移文件格式
执行 create 后会在 migrations/ 中生成形如 20260326120000_create_users_table.sql 的文件:
-- +goose Up
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- +goose Down
DROP TABLE IF EXISTS users;规则
-- +goose Up是向上迁移,-- +goose Down是回滚- 每个文件必须同时包含 Up 与 Down
- Down 应能完全撤销 Up 的变更
- 文件名时间戳决定执行顺序,不要手动修改时间戳
- 不要修改已经在生产环境执行过的迁移文件;有问题就新建一个修复型迁移
多语句事务
默认每条 SQL 独立执行。如果需要在同一个事务里跑多条:
-- +goose Up
-- +goose StatementBegin
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
amount DECIMAL(10,2) NOT NULL
);
INSERT INTO orders (user_id, amount) VALUES (1, 100.00);
-- +goose StatementEnd
-- +goose Down
DROP TABLE IF EXISTS orders;启动时自动迁移
如果在 AppProvider.Servers() 中启用了 migration.Migration{},服务每次启动都会执行一次 goose.Up(等价于 CLI 的 up 命令)。这在 Kubernetes 等场景下非常方便:
- 部署 = 重启 = 自动迁移
- 多实例同时启动时,Goose 利用数据库锁保证迁移只跑一次
如果你不希望启动时自动迁移(例如想在 CI 里独立执行),只需在 config/app.go 的 Servers() 中注释掉 &migration.Migration{},然后通过 CLI 在部署前手动执行。
各驱动差异
| 驱动 | 迁移支持 | Goose Dialect |
|---|---|---|
mysql |
✅ | mysql |
postgresql |
✅ | postgres |
sqlite |
✅ | sqlite3 |
memory |
❌ | —— |
memory 驱动用于 Demo 与单元测试,不支持持久化迁移。CLI 在检测到 database.driver=memory 时会直接报错。
实际示例
完整的"新增字段"迁移示例:
-- +goose Up
ALTER TABLE users
ADD COLUMN nickname VARCHAR(64) NOT NULL DEFAULT '' AFTER username,
ADD INDEX idx_users_nickname (nickname);
-- +goose Down
ALTER TABLE users
DROP INDEX idx_users_nickname,
DROP COLUMN nickname;"重命名表"示例(注意必须可回滚):
-- +goose Up
RENAME TABLE old_users TO users;
-- +goose Down
RENAME TABLE users TO old_users;常见问题
memory 驱动不支持版本化迁移—— 切换到 mysql / postgresql / sqlite 之一不支持的数据库驱动: xxx——database.driver必须是mysql/postgresql/sqlite之一- 服务启动后立即退出 —— 检查迁移文件中是否有 SQL 语法错误,启动日志会打印第一条失败的迁移文件名
- 多实例并发启动 —— Goose 通过
goose_db_version表 + 数据库锁保护,无需额外协调