GORM 生产环境迁移策略
生产环境数据库变更需特别谨慎,本文介绍零停机迁移策略与大表变更的优化方法。
什么是零停机迁移
零停机迁移指在数据库结构变更期间,应用持续可用、不中断服务的迁移方式。
大表变更的挑战
大表 ALTER TABLE 可能触发表级锁,阻塞读写操作。
SQL
-- MySQL 8.0 之前:大表 ALTER 可能锁表数分钟
ALTER TABLE orders ADD COLUMN status VARCHAR(20);
-- 5000 万行数据,执行时间 > 10 分钟
策略一:扩展优先原则
新增列/索引通常安全,可在线执行。
Go
// 安全:新增列不影响现有查询
func Up(db *gorm.DB) error {
return db.Migrator().AddColumn(&Order{}, "tracking_number")
}
策略二:分阶段迁移
删除或修改列应分阶段执行。
阶段 1:新增列(兼容读写)
Go
// 新增列,应用双写新旧字段
db.Migrator().AddColumn(&User{}, "new_email")
Go
// 应用代码:双写
func UpdateUser(user *User) {
user.NewEmail = user.Email // 写入新字段
db.Save(user) // 同时写入旧字段
}
阶段 2:数据迁移
后台脚本迁移历史数据。
Go
// 分批迁移,每批 1000 条
func BackfillData(db *gorm.DB) error {
batchSize := 1000
offset := 0
for {
var users []User
db.Where("new_email IS NULL").
Offset(offset).Limit(batchSize).Find(&users)
if len(users) == 0 {
break
}
for _, u := range users {
u.NewEmail = u.Email
db.Save(&u)
}
offset += batchSize
}
return nil
}
阶段 3:切换读取
应用切换读取新字段,确认无异常。
阶段 4:删除旧列
确认新字段稳定后删除旧列。
Go
db.Migrator().DropColumn(&User{}, "email")
策略三:在线 DDL 工具
MySQL 5.6+ 支持 ALGORITHM=INPLACE,大表变更不锁表。
SQL
-- 使用 INPLACE 算法,不阻塞 DML
ALTER TABLE orders ADD COLUMN status VARCHAR(20), ALGORITHM=INPLACE, LOCK=NONE;
GORM 中直接执行:
Go
db.Exec("ALTER TABLE orders ADD COLUMN status VARCHAR(20), ALGORITHM=INPLACE, LOCK=NONE")
策略四:分批执行
大变更拆分为小批次执行。
Go
// 分批创建分区
for i := 0; i < 12; i++ {
db.Exec(fmt.Sprintf(
"ALTER TABLE orders ADD PARTITION (PARTITION p%d VALUES LESS THAN (%d))",
i, i+1,
))
}
- **迁移窗口:**尽量选择低峰期执行,即使支持在线变更。
- **回滚预案:**每次迁移前准备好回滚脚本。
- **监控:**迁移期间密切关注锁等待、慢查询、CPU/IO 指标。
- **测试环境验证:**迁移脚本必须在测试环境充分验证后再上生产。
要点总结
| 策略 | 适用场景 | 核心要点 |
|---|---|---|
| 扩展优先 | 新增列/索引 | 直接执行,风险低 |
| 分阶段迁移 | 删除/修改列 | 双写→迁移→切换→清理 |
| 在线 DDL | MySQL 5.6+ 大表 | 使用 ALGORITHM=INPLACE |
| 分批执行 | 超大变更 | 拆分为小批次逐步执行 |
- 生产环境迁移必须零停机,确保应用持续可用。
- 大表变更优先使用在线 DDL 或分阶段迁移。
- 删除列必须分阶段:先双写再清理。
- 迁移前充分测试,准备好回滚预案。
存放路径: D:\git2\jwdev\articles\GORM\专家\数据迁移与版本管理\生产环境迁移策略.md
📝 发现内容有误?点击此处直接编辑