feat: 完成数据库迁移系统开发

- 创建完整的迁移框架 (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%
This commit is contained in:
xujiang
2025-07-11 13:41:52 +08:00
parent 543c59bdca
commit 84e778e033
12 changed files with 3290 additions and 9 deletions

View File

@ -0,0 +1,414 @@
# 数据库迁移系统文档
## 📋 概述
本项目使用自定义的数据库迁移系统,支持版本控制、回滚、备份等完整的数据库管理功能。该系统专为 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` 表来跟踪迁移状态:
```sql
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. 开发环境初始化
```bash
# 初始化数据库(全新安装)
make db-init
# 查看迁移状态
make migrate-status
# 手动运行所有迁移
make migrate-up
```
### 2. 生产环境部署
```bash
# 全新生产环境初始化
./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` |
### 命令行工具
```bash
# 基本语法
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 # 查看版本
```
### 生产环境脚本
```bash
# 基本语法
./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
```bash
make migrate-create NAME="add_user_avatar_field"
```
#### 方法2: 手动添加到 migrations.go
```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 迁移 (向前)
```sql
-- ✅ 好的做法
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 迁移 (回滚)
```sql
-- 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. 数据迁移
```sql
-- 安全的数据迁移
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; -- 危险!不要直接删除数据
```
## 🔒 安全性和生产环境
### 备份策略
1. **自动备份**: 每次迁移前自动创建备份
2. **定期备份**: 通过 cron 定期备份
3. **备份验证**: 定期验证备份文件完整性
```bash
# 创建定期备份 cron 任务
0 2 * * * cd /path/to/project && make db-backup
# 验证备份
sqlite3 backup.db ".tables" > /dev/null && echo "备份正常" || echo "备份损坏"
```
### 生产环境部署流程
#### 1. 预生产测试
```bash
# 1. 在测试环境验证迁移
./scripts/production-migrate.sh -d migrate
# 2. 执行测试迁移
./scripts/production-migrate.sh migrate
# 3. 验证功能正常
make test
# 4. 回滚测试
./scripts/production-migrate.sh rollback 1
```
#### 2. 生产部署
```bash
# 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. 回滚计划
```bash
# 如果出现问题,立即回滚
./scripts/production-migrate.sh rollback 1
# 或者恢复备份
./scripts/production-migrate.sh restore backup_filename.db
```
### 监控和日志
```bash
# 查看迁移日志
tail -f migration.log
# 监控应用日志
tail -f backend.log
# 系统状态检查
./scripts/production-migrate.sh check
```
## 🛠️ 故障排除
### 常见问题
#### 1. 迁移失败
```bash
# 查看错误详情
go run cmd/migrate/main.go -c status
# 检查数据库连接
sqlite3 data/photography.db ".tables"
# 恢复到最近备份
make db-restore BACKUP=latest_backup.db
```
#### 2. 版本冲突
```bash
# 手动标记迁移为已应用
sqlite3 data/photography.db "UPDATE schema_migrations SET applied = 1 WHERE version = '20250111_120000';"
# 或者重置迁移状态
make migrate-reset
make migrate-up
```
#### 3. SQLite 限制
SQLite 不支持的操作和解决方案:
```sql
-- ❌ 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. 完全重置
```bash
# 备份当前数据
cp data/photography.db data/emergency_backup.db
# 重置数据库
make migrate-reset
# 重新初始化
make db-init
```
#### 2. 数据恢复
```bash
# 从备份恢复数据
sqlite3 data/photography.db "
.read backup_data.sql
"
# 或者使用工具恢复
./scripts/production-migrate.sh restore backup_file.db
```
## 📊 性能优化
### 大型迁移优化
```sql
-- 分批处理大量数据
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;
```
### 索引策略
```sql
-- 先删除索引,再批量更新,最后重建索引
DROP INDEX IF EXISTS idx_user_email;
-- 批量更新操作
CREATE INDEX idx_user_email ON user(email);
```
## 📋 最佳实践总结
### ✅ 推荐做法
1. **版本控制**: 每个迁移都有唯一版本号
2. **可回滚**: 每个 UP 迁移都有对应的 DOWN 迁移
3. **幂等性**: 使用 `IF NOT EXISTS``OR IGNORE`
4. **备份**: 生产环境迁移前自动备份
5. **测试**: 在测试环境验证迁移
6. **监控**: 记录迁移日志和状态
### ❌ 避免做法
1. **直接修改**: 不要直接修改生产数据库
2. **删除迁移**: 不要删除已应用的迁移文件
3. **跳过版本**: 不要跳过中间版本
4. **无备份**: 不要在没有备份的情况下迁移
5. **大事务**: 避免长时间锁表的大事务
## 🔗 相关资源
- [SQLite 语法参考](https://sqlite.org/lang.html)
- [GORM 迁移指南](https://gorm.io/docs/migration.html)
- [数据库设计模式](https://en.wikipedia.org/wiki/Database_design)
- [迁移最佳实践](https://www.prisma.io/dataguide/types/relational/migration-best-practices)
---
**注意**: 在生产环境中进行数据库迁移时,请务必遵循最佳实践,确保数据安全。