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