Go语言数据库:数据库迁移
1. 迁移基础概念
数据库迁移(Migration)用于管理数据库结构的版本变更,确保开发、测试、生产环境的数据库模式一致。常见工具包括Golang Migrate和GORM自带的迁移功能。
- 版本控制:每个迁移文件有唯一版本号,按顺序执行
- 回滚支持:可撤销已执行的迁移操作
- 自动化集成:可与CI/CD流程结合,确保环境一致性
2. Golang Migrate工具
Golang Migrate是独立于ORM的迁移工具,支持SQL和Go脚本,适用于原生SQL项目:
// 安装工具
go install -tags 'postgres mysql sqlserver' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
// 初始化迁移目录
migrate create -ext sql -dir db/migrations -seq create_users_table
// 生成的迁移文件(向上操作)
-- +migrate Up
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
);
// 向下操作(回滚)
-- +migrate Down
DROP TABLE users;
执行迁移命令:
// 迁移到最新版本
migrate -path db/migrations -database "postgres://user:pass@localhost:5432/db?sslmode=disable" up
// 回滚上一个版本
migrate -path db/migrations -database "..." down 1
3. GORM自动迁移
GORM提供`AutoMigrate`方法自动同步结构体到数据库表,适合快速开发但需注意生产环境慎用:
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string `gorm:"size:255"`
Email string `gorm:"uniqueIndex"`
}
func main() {
dsn := "root:password@tcp(localhost:3306)/db?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("连接数据库失败:" + err.Error())
}
// 自动迁移(创建表/添加缺失字段/修改字段类型)
err = db.AutoMigrate(&User{})
if err != nil {
panic("迁移失败:" + err.Error())
}
}
注意事项:
- 不会删除未使用的字段(避免数据丢失)
- 支持通过标签定义索引、字段类型等(如`gorm:"uniqueIndex"`)
- 生产环境建议结合手动迁移脚本
4. 迁移最佳实践
为确保迁移安全可靠,建议遵循以下实践:
- 小步迁移:每次迁移仅做少量变更,便于回滚
- 测试先行:在测试环境验证迁移和回滚操作
- 版本控制:迁移文件与代码一起提交到版本库
- 记录日志:记录迁移时间、执行用户、影响的表和字段
-- +migrate Up
ALTER TABLE users ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
-- +migrate Down
ALTER TABLE users DROP COLUMN created_at;
5. 高级迁移场景
处理复杂变更(如数据迁移、外键约束)时,需结合事务和分步操作:
-- +migrate Up
BEGIN;
-- 创建新表
CREATE TABLE new_users (
id SERIAL PRIMARY KEY,
full_name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
);
-- 复制数据(旧表name字段拆分为first_name和last_name)
INSERT INTO new_users (full_name, email)
SELECT CONCAT(first_name, ' ', last_name), email FROM old_users;
-- 删除旧表并重命名新表
DROP TABLE old_users;
ALTER TABLE new_users RENAME TO users;
COMMIT;
-- +migrate Down
BEGIN;
-- 创建旧表结构
CREATE TABLE old_users (
id SERIAL PRIMARY KEY,
first_name VARCHAR(128) NOT NULL,
last_name VARCHAR(128) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
);
-- 拆分full_name为first_name和last_name(简单示例,实际需更复杂逻辑)
INSERT INTO old_users (first_name, last_name, email)
SELECT SPLIT_PART(full_name, ' ', 1), SPLIT_PART(full_name, ' ', 2), email FROM users;
-- 删除新表并重命名旧表
DROP TABLE users;
ALTER TABLE old_users RENAME TO users;
COMMIT;
关键点:
- 使用事务确保原子性(失败时自动回滚)
- 处理大数据量时需分批操作(避免锁表)
- 外键约束需在数据迁移完成后添加