Files
photography/backend/docs/DATABASE_MIGRATION.md
xujiang 84e778e033 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%
2025-07-11 13:41:52 +08:00

9.9 KiB
Raw Blame History

数据库迁移系统文档

📋 概述

本项目使用自定义的数据库迁移系统,支持版本控制、回滚、备份等完整的数据库管理功能。该系统专为 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; -- 危险!不要直接删除数据

🔒 安全性和生产环境

备份策略

  1. 自动备份: 每次迁移前自动创建备份
  2. 定期备份: 通过 cron 定期备份
  3. 备份验证: 定期验证备份文件完整性
# 创建定期备份 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);

📋 最佳实践总结

推荐做法

  1. 版本控制: 每个迁移都有唯一版本号
  2. 可回滚: 每个 UP 迁移都有对应的 DOWN 迁移
  3. 幂等性: 使用 IF NOT EXISTSOR IGNORE
  4. 备份: 生产环境迁移前自动备份
  5. 测试: 在测试环境验证迁移
  6. 监控: 记录迁移日志和状态

避免做法

  1. 直接修改: 不要直接修改生产数据库
  2. 删除迁移: 不要删除已应用的迁移文件
  3. 跳过版本: 不要跳过中间版本
  4. 无备份: 不要在没有备份的情况下迁移
  5. 大事务: 避免长时间锁表的大事务

🔗 相关资源


注意: 在生产环境中进行数据库迁移时,请务必遵循最佳实践,确保数据安全。