数据库迁移与版本控制 Skill
描述
这个 skill 帮助开发者规范地管理数据库结构的变更。通过代码化的迁移脚本(Migrations),确保数据库变更在开发、测试、生产环境中的一致性、可追溯性和安全性,避免手动执行 SQL 带来的风险。
何时使用
在以下场景中使用这个 skill:
- AI 协助设计或修改数据库表结构时
- 用户需要添加、删除字段或索引时
- 项目需要引入数据库版本控制工具(如 golang-migrate, goose, gorm migration)时
- 用户询问如何实现零宕机(Zero-downtime)的数据库变更时
核心设计原则
1. 迁移脚本规范 (Migration Scripts)
所有的 DDL(数据定义语言)和必要的 DML(数据操作语言)必须通过版本化的迁移脚本执行。
- 命名规范:
版本号_描述.up.sql和版本号_描述.down.sql- 版本号建议使用时间戳:
20231025143000_add_user_status.up.sql - 描述应简明扼要,使用下划线分隔。
- 版本号建议使用时间戳:
- 成对出现:每个
up脚本(升级)必须有一个对应的down脚本(回滚)。 - 不可变性:一旦迁移脚本提交并合并到主分支,绝对禁止修改该脚本。如果需要调整,必须创建一个新的迁移脚本。
2. 零宕机变更策略 (Zero-Downtime Migrations)
在生产环境中,直接修改表结构(特别是大表)可能导致锁表和业务中断。必须采用向后兼容的变更策略。
场景 A:添加新字段
- 步骤 1:添加新字段,必须允许 NULL 或设置默认值。
- 步骤 2:更新代码,使其能够读写新字段。
场景 B:重命名字段或修改字段类型
严禁直接使用 ALTER TABLE RENAME/MODIFY。
- 步骤 1:添加新字段(新名称或新类型)。
- 步骤 2:更新代码,实现双写(同时写入老字段和新字段),读取时优先读老字段。
- 步骤 3:执行数据迁移脚本(Backfill),将老字段的数据刷入新字段。
- 步骤 4:更新代码,改为只读写新字段。
- 步骤 5:在未来的版本中,通过迁移脚本删除老字段。
场景 C:添加索引
- 在 MySQL/PostgreSQL 中,添加索引可能阻塞写操作。
- 必须使用并发/在线建索引语法:
- MySQL:
ALGORITHM=INPLACE, LOCK=NONE - PostgreSQL:
CREATE INDEX CONCURRENTLY
- MySQL:
3. 常见工具推荐
在 Go 生态中,推荐使用以下工具管理迁移:
- golang-migrate/migrate:最流行,支持 CLI 和代码内嵌。
- pressly/goose:支持 SQL 和 Go 代码编写迁移逻辑。
4. 迁移脚本编写示例 (以 MySQL 为例)
UP 脚本 (20231025143000_add_user_status.up.sql):
ALTER TABLE `users`
ADD COLUMN `status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '状态: 1-正常 2-封禁' AFTER `email`;
CREATE INDEX `idx_status` ON `users` (`status`);
DOWN 脚本 (20231025143000_add_user_status.down.sql):
ALTER TABLE `users` DROP INDEX `idx_status`;
ALTER TABLE `users` DROP COLUMN `status`;
AI 交互指导
当 AI 协助生成数据库变更 SQL 或迁移代码时,必须:
- 强制要求提供 UP 和 DOWN 两部分逻辑。
- 检查添加的字段是否具备合理的默认值或允许 NULL,以防破坏现有插入逻辑。
- 如果检测到用户试图直接重命名字段或修改类型,必须警告其风险,并建议使用上述的“双写+数据回填”策略。
- 提醒用户在建索引时注意锁表风险,推荐使用在线建索引语法。