migration

package
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 28, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

README

数据库迁移

本目录包含数据库迁移文件和迁移工具。

⚠️ 重要说明:迁移不会覆盖数据

常见误解:每次启动都执行迁移,会不会覆盖线上数据?

答案:不会! 迁移使用 版本控制机制

┌─────────────────────────────────────────┐
│  schema_migrations 表(自动创建)        │
├─────────────────────────────────────────┤
│  version  │  dirty                      │
│  1        │  false   ← 已执行版本       │
└─────────────────────────────────────────┘

第 1 次启动 → 执行 v1 迁移 → 记录 version=1 ✅
第 2 次启动 → 检查 version=1 → 跳过(0 SQL 执行)✅
第 3 次启动 → 检查 version=1 → 跳过(0 SQL 执行)✅
新版本发布 → 检查 version=1 → 仅执行 v2 → 记录 version=2 ✅

关键点

  • ✅ 迁移是增量的,不是全量的
  • ✅ 每个版本只执行一次
  • ✅ 后续启动会跳过已执行的版本
  • ✅ 不会删除或覆盖现有数据

📁 目录结构

migration/
├── migrate.go              # 迁移工具实现
├── migrations/             # 迁移 SQL 文件(嵌入到二进制)
│   ├── 000001_init_schema.up.sql      # 初始化表结构
│   ├── 000001_init_schema.down.sql    # 回滚表结构
│   ├── 000005_bootstrap_system_data.up.sql   # 最小系统初始化数据
│   ├── 000005_bootstrap_system_data.down.sql # 回滚最小系统初始化数据
│   └── ...
└── README.md               # 本文件

🚀 快速开始

1. 安装依赖
# 添加 golang-migrate 依赖
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/iofs

# (可选)安装 CLI 工具,用于创建迁移文件
go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
2. 在应用中使用
package main

import (
    "database/sql"
    "fmt"
    
    "github.com/FangcunMount/iam/internal/pkg/migration"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // 1. 连接数据库
    db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/iam")
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // 2. 配置迁移器
    cfg := &migration.Config{
        Enabled:  true,              // 启用自动迁移
        Database: "iam",   // 数据库名称
    }

    // 3. 创建迁移器并执行
    migrator := migration.NewMigrator(db, cfg)
    if version, applied, err := migrator.Run(); err != nil {
        panic(err)
    } else if applied {
        fmt.Printf("migrated to version %d\n", version)
    } else {
        fmt.Printf("database already up to date (version %d)\n", version)
    }

    // 4. 启动应用...
}
3. 创建新的迁移
# 使用 migrate CLI 创建迁移文件
migrate create -ext sql -dir internal/pkg/migration/migrations -seq add_new_feature

# 生成的文件:
# - 000003_add_new_feature.up.sql   (升级脚本)
# - 000003_add_new_feature.down.sql (回滚脚本)

编辑生成的文件:

000003_add_new_feature.up.sql:

-- 添加新功能
ALTER TABLE iam_users ADD COLUMN nickname VARCHAR(64) COMMENT '昵称';

000003_add_new_feature.down.sql:

-- 回滚新功能
ALTER TABLE iam_users DROP COLUMN nickname;

🔧 高级用法

手动控制迁移
// 获取当前版本
version, dirty, err := migrator.Version()

// 回滚最近的一次迁移
err = migrator.Rollback()
环境变量配置
# configs/apiserver.prod.yaml
mysql:
  host: ${MYSQL_HOST:127.0.0.1}
  port: ${MYSQL_PORT:3306}
  database: ${MYSQL_DATABASE:iam}
  username: ${MYSQL_USER:root}
  password: ${MYSQL_PASSWORD:}

migration:
  enabled: ${MIGRATION_ENABLED:true}
Docker 部署
# Dockerfile
FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY . .

# 构建(迁移文件会被嵌入)
RUN go build -o /apiserver ./cmd/apiserver

# 运行阶段
FROM alpine:latest
COPY --from=builder /apiserver .
CMD ["./apiserver"]

容器启动时会自动执行迁移,无需手动操作。

📊 迁移表

golang-migrate 会自动创建 schema_migrations 表来追踪版本:

mysql> SELECT * FROM schema_migrations;
+---------+-------+
| version | dirty |
+---------+-------+
|       2 |     0 |
+---------+-------+
  • version: 当前数据库版本号
  • dirty: 是否处于中间状态(0=正常,1=异常需手动修复)

🔐 安全注意事项

生产环境
  1. 备份优先

    # 迁移前自动备份
    mysqldump iam > backup_$(date +%Y%m%d_%H%M%S).sql
    
  2. 权限分离

    • 应用账号:只需 SELECT, INSERT, UPDATE, DELETE
    • 迁移账号:需要 CREATE, DROP, ALTER 等 DDL 权限
  3. 测试迁移

    • 在测试环境先验证
    • 确保 down 脚本能正确回滚
  4. 金丝雀发布

    • 先在一个实例上执行
    • 验证成功后再推广
开发环境
# 重置数据库到初始状态
cd scripts/sql
./reset-db.sh

# 应用会在启动时自动执行迁移
go run cmd/apiserver/apiserver.go

📚 参考文档

❓ 常见问题

Q: 为什么要使用迁移工具?

A: 在容器化环境中,应用只打包二进制文件。使用 embed.FS 可以将 SQL 文件嵌入到二进制中,启动时自动执行,无需挂载外部文件。

Q: 如何处理 dirty 状态?

A: Dirty 状态表示迁移中途失败。需要:

  1. 检查日志确定失败原因
  2. 手动修复数据库到一致状态
  3. 更新 schema_migrations 表的 dirty 字段为 0
UPDATE schema_migrations SET dirty = 0 WHERE version = X;
Q: 生产环境如何禁用自动迁移?

A: 设置环境变量:

export MIGRATION_ENABLED=false
Q: 如何在 Kubernetes 中使用?

A: 使用 Init Container:

initContainers:
- name: migrate
  image: your-app:latest
  command: ["/app/migrate-only"]  # 特殊命令只执行迁移

或者让应用启动时自动执行(推荐)。

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	Enabled  bool   // 是否启用自动迁移
	Database string // 数据库名称
}

Config 迁移配置

type Migrator

type Migrator struct {
	// contains filtered or unexported fields
}

Migrator 数据库迁移器

func NewMigrator

func NewMigrator(db *sql.DB, config *Config) *Migrator

NewMigrator 创建迁移器

func (*Migrator) Rollback

func (m *Migrator) Rollback() error

Rollback 回滚最近的一次迁移

func (*Migrator) Run

func (m *Migrator) Run() (uint, bool, error)

Run 执行数据库迁移并返回最新版本以及是否执行了迁移

工作流程: 1. 检查是否启用迁移 2. 创建 migrate 实例 3. 获取当前版本 4. 执行迁移到最新版本 5. 返回最新版本及是否执行了迁移

func (*Migrator) Version

func (m *Migrator) Version() (uint, bool, error)

Version 获取当前数据库版本

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL