- 创建完整的迁移框架 (pkg/migration/) - 版本管理系统,时间戳版本号 (YYYYMMDD_HHMMSS) - 事务安全的上下迁移机制 (Up/Down) - 迁移状态跟踪和记录 (migration_records 表) - 命令行迁移工具 (cmd/migrate/main.go) - 生产环境迁移脚本 (scripts/production-migrate.sh) - 生产环境初始化脚本 (scripts/init-production-db.sh) - 迁移测试脚本 (scripts/test-migration.sh) - Makefile 集成 (migrate-up, migrate-down, migrate-status) - 5个预定义迁移 (基础表、默认数据、元数据、收藏、用户资料) - 自动备份机制、预览模式、详细日志 - 完整文档 (docs/DATABASE_MIGRATION.md) 任务13完成,项目完成率达到42.5%
9.9 KiB
9.9 KiB
数据库迁移系统文档
📋 概述
本项目使用自定义的数据库迁移系统,支持版本控制、回滚、备份等完整的数据库管理功能。该系统专为 SQLite 优化,同时兼容 MySQL 和 PostgreSQL。
🏗️ 系统架构
核心组件
pkg/migration/
├── migration.go # 迁移管理器核心逻辑
├── migrations.go # 所有迁移定义
└── README.md # 迁移开发指南
cmd/migrate/
└── main.go # 命令行工具
scripts/
├── production-migrate.sh # 生产环境迁移脚本
└── init-production-db.sh # 生产环境初始化脚本
迁移记录表
系统会自动创建 schema_migrations 表来跟踪迁移状态:
CREATE TABLE schema_migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
version VARCHAR(255) UNIQUE NOT NULL,
description VARCHAR(500),
applied BOOLEAN DEFAULT FALSE,
applied_at TIMESTAMP,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
🚀 快速开始
1. 开发环境初始化
# 初始化数据库(全新安装)
make db-init
# 查看迁移状态
make migrate-status
# 手动运行所有迁移
make migrate-up
2. 生产环境部署
# 全新生产环境初始化
./scripts/init-production-db.sh
# 生产环境迁移
./scripts/production-migrate.sh migrate
# 预览模式(不实际执行)
./scripts/production-migrate.sh -d migrate
📖 命令参考
Makefile 命令
| 命令 | 描述 | 示例 |
|---|---|---|
migrate-status |
查看迁移状态 | make migrate-status |
migrate-up |
运行所有待处理迁移 | make migrate-up |
migrate-down |
回滚指定步数迁移 | make migrate-down STEPS=1 |
migrate-reset |
重置整个数据库 | make migrate-reset |
migrate-create |
创建新迁移模板 | make migrate-create NAME=add_user_field |
migrate-version |
显示最新迁移版本 | make migrate-version |
db-init |
初始化数据库 | make db-init |
db-backup |
创建数据库备份 | make db-backup |
db-restore |
恢复数据库备份 | make db-restore BACKUP=filename |
命令行工具
# 基本语法
go run cmd/migrate/main.go [选项] -c 命令
# 查看帮助
go run cmd/migrate/main.go -h
# 常用命令
go run cmd/migrate/main.go -c status # 查看状态
go run cmd/migrate/main.go -c up # 运行迁移
go run cmd/migrate/main.go -c down -s 1 # 回滚1步
go run cmd/migrate/main.go -c migrate -s 2 # 运行2个迁移
go run cmd/migrate/main.go -c version # 查看版本
生产环境脚本
# 基本语法
./scripts/production-migrate.sh [选项] 命令
# 常用命令
./scripts/production-migrate.sh status # 查看状态
./scripts/production-migrate.sh migrate # 执行迁移
./scripts/production-migrate.sh rollback 1 # 回滚1步
./scripts/production-migrate.sh backup # 创建备份
./scripts/production-migrate.sh check # 系统检查
# 预览模式
./scripts/production-migrate.sh -d migrate # 预览迁移
./scripts/production-migrate.sh -v status # 详细输出
./scripts/production-migrate.sh -f migrate # 强制执行
📝 迁移开发
迁移命名规范
版本号格式: YYYYMMDD_HHMMSS
示例: 20250111_120000
描述规范:
- 动词开头: Create, Add, Update, Remove, Drop
- 清晰简洁: Create user table, Add email index
- 避免缩写: 使用完整单词
创建新迁移
方法1: 使用 Makefile
make migrate-create NAME="add_user_avatar_field"
方法2: 手动添加到 migrations.go
{
Version: "20250111_120000",
Description: "Add avatar field to user table",
Timestamp: time.Date(2025, 1, 11, 12, 0, 0, 0, time.UTC),
UpSQL: `
ALTER TABLE user ADD COLUMN avatar VARCHAR(255) DEFAULT '';
CREATE INDEX IF NOT EXISTS idx_user_avatar ON user(avatar);
`,
DownSQL: `
DROP INDEX IF EXISTS idx_user_avatar;
-- SQLite 不支持 DROP COLUMN,需要重建表
CREATE TABLE user_temp AS SELECT id, username, password, email, status, created_at, updated_at FROM user;
DROP TABLE user;
ALTER TABLE user_temp RENAME TO user;
`,
},
迁移编写最佳实践
1. UP 迁移 (向前)
-- ✅ 好的做法
ALTER TABLE user ADD COLUMN IF NOT EXISTS avatar VARCHAR(255) DEFAULT '';
CREATE INDEX IF NOT EXISTS idx_user_avatar ON user(avatar);
INSERT OR IGNORE INTO category (name) VALUES ('新分类');
-- ❌ 避免的做法
ALTER TABLE user ADD COLUMN avatar VARCHAR(255); -- 没有 IF NOT EXISTS
CREATE INDEX idx_user_avatar ON user(avatar); -- 没有 IF NOT EXISTS
INSERT INTO category (name) VALUES ('新分类'); -- 没有 OR IGNORE
2. DOWN 迁移 (回滚)
-- SQLite 回滚模式(重建表)
CREATE TABLE user_temp (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
-- 注意:不包含新添加的 avatar 字段
status INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO user_temp (id, username, password, email, status, created_at, updated_at)
SELECT id, username, password, email, status, created_at, updated_at FROM user;
DROP TABLE user;
ALTER TABLE user_temp RENAME TO user;
-- 重新创建索引
CREATE INDEX IF NOT EXISTS idx_user_username ON user(username);
CREATE INDEX IF NOT EXISTS idx_user_email ON user(email);
3. 数据迁移
-- 安全的数据迁移
UPDATE user SET status = 1 WHERE status IS NULL OR status = 0;
UPDATE photo SET category_id = 1 WHERE category_id IS NULL;
-- 避免危险操作
-- DELETE FROM user; -- 危险!不要直接删除数据
🔒 安全性和生产环境
备份策略
- 自动备份: 每次迁移前自动创建备份
- 定期备份: 通过 cron 定期备份
- 备份验证: 定期验证备份文件完整性
# 创建定期备份 cron 任务
0 2 * * * cd /path/to/project && make db-backup
# 验证备份
sqlite3 backup.db ".tables" > /dev/null && echo "备份正常" || echo "备份损坏"
生产环境部署流程
1. 预生产测试
# 1. 在测试环境验证迁移
./scripts/production-migrate.sh -d migrate
# 2. 执行测试迁移
./scripts/production-migrate.sh migrate
# 3. 验证功能正常
make test
# 4. 回滚测试
./scripts/production-migrate.sh rollback 1
2. 生产部署
# 1. 备份生产数据库
./scripts/production-migrate.sh backup
# 2. 执行迁移
./scripts/production-migrate.sh migrate
# 3. 验证迁移结果
./scripts/production-migrate.sh status
# 4. 重启应用服务
sudo systemctl restart photography-api
3. 回滚计划
# 如果出现问题,立即回滚
./scripts/production-migrate.sh rollback 1
# 或者恢复备份
./scripts/production-migrate.sh restore backup_filename.db
监控和日志
# 查看迁移日志
tail -f migration.log
# 监控应用日志
tail -f backend.log
# 系统状态检查
./scripts/production-migrate.sh check
🛠️ 故障排除
常见问题
1. 迁移失败
# 查看错误详情
go run cmd/migrate/main.go -c status
# 检查数据库连接
sqlite3 data/photography.db ".tables"
# 恢复到最近备份
make db-restore BACKUP=latest_backup.db
2. 版本冲突
# 手动标记迁移为已应用
sqlite3 data/photography.db "UPDATE schema_migrations SET applied = 1 WHERE version = '20250111_120000';"
# 或者重置迁移状态
make migrate-reset
make migrate-up
3. SQLite 限制
SQLite 不支持的操作和解决方案:
-- ❌ SQLite 不支持
ALTER TABLE user DROP COLUMN old_field;
ALTER TABLE user MODIFY COLUMN name VARCHAR(100);
-- ✅ 解决方案:重建表
CREATE TABLE user_new (...);
INSERT INTO user_new SELECT ... FROM user;
DROP TABLE user;
ALTER TABLE user_new RENAME TO user;
紧急恢复
1. 完全重置
# 备份当前数据
cp data/photography.db data/emergency_backup.db
# 重置数据库
make migrate-reset
# 重新初始化
make db-init
2. 数据恢复
# 从备份恢复数据
sqlite3 data/photography.db "
.read backup_data.sql
"
# 或者使用工具恢复
./scripts/production-migrate.sh restore backup_file.db
📊 性能优化
大型迁移优化
-- 分批处理大量数据
UPDATE user SET new_field = 'default' WHERE id BETWEEN 1 AND 1000;
UPDATE user SET new_field = 'default' WHERE id BETWEEN 1001 AND 2000;
-- ...
-- 使用事务
BEGIN TRANSACTION;
-- 批量操作
COMMIT;
索引策略
-- 先删除索引,再批量更新,最后重建索引
DROP INDEX IF EXISTS idx_user_email;
-- 批量更新操作
CREATE INDEX idx_user_email ON user(email);
📋 最佳实践总结
✅ 推荐做法
- 版本控制: 每个迁移都有唯一版本号
- 可回滚: 每个 UP 迁移都有对应的 DOWN 迁移
- 幂等性: 使用
IF NOT EXISTS和OR IGNORE - 备份: 生产环境迁移前自动备份
- 测试: 在测试环境验证迁移
- 监控: 记录迁移日志和状态
❌ 避免做法
- 直接修改: 不要直接修改生产数据库
- 删除迁移: 不要删除已应用的迁移文件
- 跳过版本: 不要跳过中间版本
- 无备份: 不要在没有备份的情况下迁移
- 大事务: 避免长时间锁表的大事务
🔗 相关资源
注意: 在生产环境中进行数据库迁移时,请务必遵循最佳实践,确保数据安全。