golang开发中好用的数据库迁移工具

golang-migrate/migrate —— 它是 Go 社区最主流、生产级、轻量且与框架无关的迁移方案,“不用反射、手写 SQL”的风格。


✅ 为什么选 golang-migrate/migrate

特性说明
纯 SQL 迁移你写 .sql 文件,完全掌控 DDL/DML,无黑盒 ORM
命令行 + 库双模式可 CLI 执行,也可嵌入 Gin 启动时自动运行
支持 MySQL/PostgreSQL 等你的 MySQL 完美兼容
版本控制自动生成 schema_migrations 表记录版本
Go 原生无 CGO 依赖,编译简单

⚠️ 不推荐 GORM 的 AutoMigrate(黑盒、难回滚)或 sqlc(只生成查询,不管理结构变更)


一、安装 CLI 工具(开发用)

# macOS (Homebrew)
brew install golang-migrate

# Linux / Windows: 下载二进制
# https://github.com/golang-migrate/migrate/releases

验证:

migrate -version
# 输出如: v4.17.0

二、在 Gin 项目中集成(自动迁移)

1. 添加 Go 依赖

go get -u github.com/golang-migrate/migrate/v4
go get -u github.com/golang-migrate/migrate/v4/database/mysql
go get -u github.com/golang-migrate/migrate/v4/source/file

2. 创建迁移目录

mkdir -p migrations

3. 生成第一个迁移文件(示例:创建 users 表)

migrate create -ext sql -dir migrations -seq create_users_table

生成:

migrations/
├── 000001_create_users_table.up.sql
└── 000001_create_users_table.down.sql

4. 编写迁移 SQL

migrations/000001_create_users_table.up.sql

CREATE TABLE `users` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `user_uid` VARCHAR(50) NOT NULL UNIQUE,
  `gold` INT DEFAULT 0,
  `last_login` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

migrations/000001_create_users_table.down.sql

DROP TABLE IF EXISTS `users`;

💡 每次加新表/改字段,都用 migrate create 生成新文件


三、在 Gin 启动时自动执行迁移

修改 main.go

// main.go
package main

import (
    "database/sql"
    "log"
    "net/url"

    "github.com/gin-gonic/gin"
    "github.com/golang-migrate/migrate/v4"
    _ "github.com/golang-migrate/migrate/v4/database/mysql"
    _ "github.com/golang-migrate/migrate/v4/source/file"
)

func main() {
    // 1. 初始化数据库连接(和你之前一样)
    db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/game_db?parseTime=true")
    if err != nil {
        log.Fatal("DB connect failed:", err)
    }
    defer db.Close()

    // 2. 执行数据库迁移(关键!)
    runMigrations(db)

    // 3. 启动 Gin
    r := gin.Default()
    // ... 注册路由
    r.Run(":8080")
}

func runMigrations(db *sql.DB) {
    // 构造 MySQL DSN for migrate
    dsn := "user:password@tcp(localhost:3306)/game_db?multiStatements=true"

    // 注意:必须 URL-encode 密码中的特殊字符(如 @, /)
    // 更安全的方式:
    cfg := mysql.Config{
        User:                 "user",
        Passwd:               "password",
        Net:                  "tcp",
        Addr:                 "localhost:3306",
        DBName:               "game_db",
        MultiStatements:      true,
        AllowNativePasswords: true,
    }
    dsn = cfg.FormatDSN()

    // 创建 migrate 实例
    m, err := migrate.New(
        "file://./migrations", // 迁移文件目录
        dsn,
    )
    if err != nil {
        log.Fatal("Migration init failed:", err)
    }
    defer m.Close()

    // 自动升级到最新版本
    if err := m.Up(); err != nil && err != migrate.ErrNoChange {
        log.Fatal("Migration failed:", err)
    }

    log.Println("✅ Database migrated successfully")
}

🔒 安全提示

  • 生产环境建议不要自动迁移,而是手动执行 CLI 命令
  • 开发/测试环境可开启自动迁移

四、常用 CLI 命令(开发时用)

命令说明
migrate -path migrations -database "mysql://..." up升级到最新
migrate -path migrations -database "mysql://..." down回滚一步
migrate -path migrations -database "mysql://..." goto 1回滚到指定版本
migrate -path migrations -database "mysql://..." version查看当前版本

示例(本地开发):

migrate -path ./migrations -database "mysql://root:123456@tcp(127.0.0.1:3306)/game_db?multiStatements=true" up

五、项目结构建议

your-gin-project/
├── main.go
├── migrations/               # ← 迁移文件目录(纳入 Git)
│   ├── 000001_create_users.up.sql
│   ├── 000001_create_users.down.sql
│   └── 000002_add_gacha_tables.up.sql
├── model/                    # 手写 Scan 的模型
├── service/
├── go.mod
└── ...

六、替代方案对比

工具优点缺点适合你吗?
golang-migrate纯 SQL、轻量、生产级需手写 SQL强烈推荐
GORM AutoMigrate代码即 schema黑盒、难回滚、类型限制多
sqlc生成类型安全查询不管理 DDL,需另配迁移工具⚠️ 需搭配 migrate
Goose类似 migrate社区活跃度较低可用,但 migrate 更主流

总结

  1. 开发阶段:用 migrate create 生成 .sql 文件,写好 up/down
  2. 本地启动 Gin:自动执行 m.Up()(仅开发环境)
  3. 上线部署:用 CLI 手动执行迁移,确保可控

Comments

No comments yet. Why don’t you start the discussion?

发表回复