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:
414
backend/docs/DATABASE_MIGRATION.md
Normal file
414
backend/docs/DATABASE_MIGRATION.md
Normal 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)
|
||||
|
||||
---
|
||||
|
||||
**注意**: 在生产环境中进行数据库迁移时,请务必遵循最佳实践,确保数据安全。
|
||||
Reference in New Issue
Block a user