- 创建完整的迁移框架 (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%
414 lines
9.9 KiB
Markdown
414 lines
9.9 KiB
Markdown
# 数据库迁移系统文档
|
||
|
||
## 📋 概述
|
||
|
||
本项目使用自定义的数据库迁移系统,支持版本控制、回滚、备份等完整的数据库管理功能。该系统专为 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)
|
||
|
||
---
|
||
|
||
**注意**: 在生产环境中进行数据库迁移时,请务必遵循最佳实践,确保数据安全。 |