--- title: Flyway 数据库迁移最佳实践 keywords: [Flyway, 数据库迁移, 版本管理, schema, migration] summary: Flyway 数据库迁移的命名规范、编写技巧、回滚策略和常见问题处理 category: infrastructure --- # Flyway 数据库迁移最佳实践 ## 命名规范 ### 标准格式 ``` V{version}__{description}.sql 示例: V001__create_user_table.sql V002__add_email_to_user.sql V003__create_order_table.sql V004__add_metadata_to_api_document.sql ``` **规则**: - `V` 大写,表示 Versioned migration - 版本号用 3 位数字(001, 002...) - 两个下划线 `__` 分隔版本号和描述 - 描述用小写字母和下划线 ### 版本号管理 ``` V001 - 初始表结构 V002 - 添加字段 V003 - 创建索引 V004 - 修改字段类型 ... ``` **建议**: - 预留版本号空间(001, 010, 020...) - 紧急修复用中间号(V005_hotfix__...) ## SQL 编写规范 ### 添加列 ```sql -- ✅ 好的写法 - 包含默认值和注释 ALTER TABLE user ADD COLUMN email VARCHAR(100) DEFAULT '' COMMENT '用户邮箱'; -- ❌ 不好的写法 - 缺少默认值 ALTER TABLE user ADD COLUMN email VARCHAR(100); -- 已有数据会是 NULL ``` ### 修改列 ```sql -- ✅ 先添加新列,再迁移数据,最后删除旧列 ALTER TABLE user ADD COLUMN new_status VARCHAR(20) DEFAULT 'active'; UPDATE user SET new_status = old_status WHERE old_status IS NOT NULL; ALTER TABLE user DROP COLUMN old_status; ALTER TABLE user CHANGE COLUMN new_status status VARCHAR(20); -- ❌ 直接修改 - 可能导致数据丢失 ALTER TABLE user MODIFY COLUMN status INT; ``` ### 创建索引 ```sql -- ✅ 指定索引名称 CREATE INDEX idx_user_email ON user(email); CREATE INDEX idx_order_user_id ON `order`(user_id); -- ❌ 不指定名称 - 自动生成的名称难以管理 CREATE INDEX ON user(email); ``` ### 外键约束 ```sql -- ✅ 命名规范 ALTER TABLE `order` ADD CONSTRAINT fk_order_user_id FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE; -- ❌ 不指定名称 ALTER TABLE `order` ADD FOREIGN KEY (user_id) REFERENCES user(id); ``` ## 幂等性保证 ### 检查表是否存在 ```sql -- 创建表前检查 CREATE TABLE IF NOT EXISTS user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL ); ``` ### 检查列是否存在 ```sql -- 添加列前检查 ALTER TABLE user ADD COLUMN IF NOT EXISTS email VARCHAR(100); -- 或使用存储过程(MySQL < 8.0) SET @col_exists = ( SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'user' AND column_name = 'email' ); SET @query = IF(@col_exists = 0, 'ALTER TABLE user ADD COLUMN email VARCHAR(100)', 'SELECT "Column exists" AS msg' ); PREPARE stmt FROM @query; EXECUTE stmt; DEALLOCATE PREPARE stmt; ``` ### 检查索引是否存在 ```sql CREATE INDEX IF NOT EXISTS idx_user_email ON user(email); ``` ## 数据迁移 ### 分批处理大表 ```sql -- ❌ 一次更新全部 - 可能锁表很久 UPDATE large_table SET status = 'active' WHERE status IS NULL; -- ✅ 分批更新 UPDATE large_table SET status = 'active' WHERE status IS NULL LIMIT 1000; -- 重复执行直到影响行数为 0 ``` ### 使用事务(DDL 语句除外) ```sql START TRANSACTION; UPDATE user SET status = 'active' WHERE status = 'enabled'; UPDATE user SET status = 'inactive' WHERE status = 'disabled'; COMMIT; ``` ## 回滚策略 ### 不支持自动回滚 Flyway 社区版不支持自动回滚,需要手动编写撤销脚本: ```sql -- V005__add_email_to_user.sql ALTER TABLE user ADD COLUMN email VARCHAR(100); -- V005__add_email_to_user.undo.sql (手动执行) ALTER TABLE user DROP COLUMN email; ``` ### 建议使用新版本修复 ```sql -- V005 出错了,不要回滚 -- 而是创建 V006 修复 -- V006__fix_user_email.sql ALTER TABLE user MODIFY COLUMN email VARCHAR(200); ``` ## 常见问题 ### 问题 1: 迁移失败后状态卡住 **症状**: ``` FlywayException: Migration failed! Schema history table shows failed migration. ``` **解决**: ```sql -- 查看迁移历史 SELECT * FROM flyway_schema_history ORDER BY installed_rank DESC; -- 删除失败记录 DELETE FROM flyway_schema_history WHERE version = '005' AND success = 0; -- 修复 SQL 脚本后重新启动 ``` ### 问题 2: Checksum 不匹配 **症状**: ``` FlywayException: Checksum mismatch for migration version 005 ``` **原因**:迁移脚本被修改了 **解决**: ```sql -- 方案 1: 修复 checksum(仅开发环境) UPDATE flyway_schema_history SET checksum = NULL WHERE version = '005'; -- 方案 2: 创建新版本(推荐) -- 不要修改已执行的迁移脚本,创建 V006 ``` ### 问题 3: 多个开发者同时创建迁移 **场景**: - 开发者 A 创建 V005 - 开发者 B 创建 V005 - 冲突! **预防**: ``` 使用时间戳版本号: V20260624001__add_user_email.sql V20260624002__add_order_index.sql ``` ## 生产环境最佳实践 ### 1. 先验证后应用 ```bash # 开发环境测试 mvn flyway:migrate # 预生产环境验证 mvn flyway:migrate -Dflyway.url=jdbc:mysql://pre-prod-db:3306/db # 生产环境应用 mvn flyway:migrate -Dflyway.url=jdbc:mysql://prod-db:3306/db ``` ### 2. 备份数据库 ```bash # 应用迁移前备份 mysqldump -u root -p superbiz_agent > backup_before_v005.sql # 应用迁移 mvn spring-boot:run # 出问题时恢复 mysql -u root -p superbiz_agent < backup_before_v005.sql ``` ### 3. 限制自动迁移 ```yaml # 生产环境配置 spring: flyway: enabled: false # 禁用自动迁移 # 手动触发 mvn flyway:migrate -Dspring.profiles.active=prod ``` ### 4. 监控迁移时间 ```sql SELECT version, description, type, installed_on, execution_time FROM flyway_schema_history ORDER BY installed_rank DESC LIMIT 10; ``` ## 工具和命令 ### Maven 命令 ```bash # 查看迁移信息 mvn flyway:info # 执行迁移 mvn flyway:migrate # 验证迁移 mvn flyway:validate # 清空数据库(危险!仅开发环境) mvn flyway:clean ``` ### 配置文件 ```yaml spring: flyway: enabled: true baseline-on-migrate: true # 已有数据库时从当前版本开始 locations: classpath:db/migration table: flyway_schema_history validate-on-migrate: true ``` ## 团队协作规范 1. **迁移脚本不可修改**:已合并的脚本禁止修改 2. **版本号递增**:新脚本必须比最新版本号大 3. **命名规范统一**:遵循 `V{version}__{description}.sql` 4. **Code Review**:迁移脚本必须经过审查 5. **测试覆盖**:每个迁移都要测试(空库 + 有数据)