尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot 多数据库迁移实战:Flyway 脚本规范与踩坑排查

Spring Boot 多数据库迁移实战:Flyway 脚本规范与踩坑排查 数据库迁移这活儿我之前也靠人肉执行过开发库里加一列生产库里漏了半夜线上查不到字段接口哗哗报错。后来把一套预约类业务系统彻底改成 Spring Boot 2 Flyway 管理才算是把这块流程收口。这个项目典型到什么程度呢——类似上门烹饪预约服务系统核心表无非是厨师档案、预约订单、评价记录但环境很分裂开发机用 H2 图省事测试环境用 PostgreSQL生产却落在 MySQL 上。不做多数据库配置光靠手写 SQL 和“谁记得谁执行”迟早出事故。这篇把我在 Spring Boot 2.7 Flyway 8.x 上实践下来的一套迁移规范、多数据库配置思路和线上排查实录整理出来。适合正在做 Spring Boot 服务端、想给项目引入数据库版本管理或者已经被“开发环境能跑、生产一上就挂”折磨过的团队参考。不吹不黑Flyway 也不是万能药但它能把数据库结构变成可以 review、可以回放、可以追责的代码资产这一步很关键。1. 内容整体设计与思路拆解1.1 为什么非要用 Flyway 而不是手工执行 SQL先说一个经常在 Spring Boot 面试题里被问到的点如果不用 Flyway你如何保证多个环境数据库结构一致很多面试者第一反应是“统一由 DBA 手工执行”这在小项目里确实能撑一阵但只要人一多、环境一多马上露馅。我就见过几个典型事故开发环境测试新功能时加了一个字段代码里已经开始用了但生产库没加上线后所有查询直接报“Unknown column”。某同事用 Navicat 手动往测试库改了表没有同步到迁移目录次日 CI 一重建测试库结构回到上周的状态。多人并行开发两个分支各自往同一个表加字段手工执行顺序不一样最后生产库字段顺序和代码里完全对不上。Flyway 解决的核心问题不是“能不能执行 SQL”而是让数据库结构变更具备版本、顺序、校验和审计能力。每次启动 Spring BootFlyway 会检查flyway_schema_history表把未执行过的迁移脚本按版本号顺序跑一遍跑完的记录写进历史表。这条链路一旦建立结构变更就变成了代码提交的一部分做 Code Review 时能看到发布流程里能留痕。1.2 多数据库配置的目标与整体思路我接手的那套预约系统技术栈并不复杂Spring Boot 2.7、JDK 11、MyBatis-Plus数据库按环境分了三类环境数据库原因本地开发H2 文件模式轻量、启动快、不需要额外安装服务自动化测试PostgreSQL与生产 SQL 方言尽量靠近验证复杂查询生产MySQL 8.0运维成熟、业务方指定我一开始以为 Flyway 支持多数据库很简单只要每个环境配一个spring.flyway.locations就行了。真正上手后才发现这里有两个问题必须先想清楚问题一不同数据库的 DDL 方言有差异。比如主键自增MySQL 写AUTO_INCREMENTPostgreSQL 写GENERATED BY DEFAULT AS IDENTITY或SERIAL布尔字段MySQL 习惯用TINYINT(1)PostgreSQL 直接用BOOLEAN。如果所有环境共用一份 SQL除非你刻意写成“最小公约数”否则总有一个环境执行失败。问题二公共数据脚本和专用结构脚本要分开。并不是所有 SQL 都跟数据库方言强相关。像初始字典数据、状态枚举、配置参数这类 DML完全可以跨库共用而建表、改字段、加索引这类 DDL最好按厂商目录隔离。所以最终思路拆成两条线厂商专用目录按h2、postgresql、mysql分目录里面放各环境自己的 DDL版本号从 V1 开始各自维护。公共目录放数据库无关的基础数据、幂等更新脚本版本号统一约定从 V100 以后开始避免和厂商目录冲突。这套设计能在“一套代码跑多个数据库”和“每种数据库自己说了算”之间取得平衡。如果你硬把三种数据库的 DDL 写进同一个 SQL 文件那才是给自己挖坑。1.3 环境贴近生产原则开发环境再轻也不能放飞多数据库配置有个重要的配套原则离生产越近的环境数据库选型越要接近生产。很多团队本地图省事全用 H2但 H2 的 SQL 兼容性只是“尽量兼容”别指望它完整模拟 MySQL 的锁、事务隔离级别和函数行为。我这边经过几次折腾后定下的准则是本地开发可以用 H2但必须开启 MySQL 或 PostgreSQL 的兼容模式且用文件库别用纯内存库否则每次重启数据都没了Flyway 反复从零跑反而掩盖了增量迁移问题。测试环境必须用同族数据库。如果生产是 MySQL测试就上 MySQL如果生产是 PostgreSQL测试也用 PostgreSQL。最好通过 Testcontainers 在 CI 里启动真实数据库跑集成测试而不是只靠 H2 测一把就上线。本地跑应用时Flyway 自动执行没问题CI 里跑测试可以用 Testcontainers 的数据库地址动态替换配置。这么说吧你不可能写出“一套 DDL 通吃所有数据库”。Flyway 要做的是把差异隔离到目录和配置层面而不是消灭差异。2. 迁移脚本命名、目录结构与 SQL 规范2.1 Flyway 脚本的命名规则和版本语义Flyway 对脚本文件名有一套强制规则很多人第一次用容易在这上面翻车。常规的版本化迁移格式是前缀_V 版本号 双下划线 描述 .sql示例V1__create_chef_profile.sql V2__create_booking_order.sql V2_1__add_order_no_index.sql几点注意版本号数字和下划线都会被当作版本的一部分V2_1和V2.1实际是同一个版本号2.1写的时候别混为了可读性我习惯用V2_1因为文件名里点号虽然在 Linux 系统没问题但某些老旧工具链对点号处理不友好。版本号必须是唯一的。Flyway 靠版本号判断“这个版本跑过没有”如果你改了已执行脚本的内容校验和会不匹配启动直接报错。描述部分建议用“动词 对象”的结构比如V3__add_chef_status_column.sql而不是V3__update.sql。描述会记录在历史表里未来排查问题时一眼能看到这个脚本干了什么。还有一类R__开头的可重复执行脚本没有版本号描述需要唯一内容变化后校验和变化启动时会重新执行。这类脚本只适合幂等的数据更新不适合结构变更结构变更是有状态推进的R 脚本每次启动都跑代价不可控。具体到命名规范我团队里的约定是前缀用途示例Vn__版本化结构迁移V4__add_ingredient_to_booking_order.sqlR__可重复执行的幂等数据更新R__update_city_coordinates.sql版本号分配方面我规定厂商迁移脚本版本号用 1~99公共脚本用 100 以上避免多个 location 合并时出现同版本号冲突。Flyway 会把配置的所有 location 合并排序两个目录都出现V2会直接报重复版本很坑。2.2 迁移脚本不可变性这是铁律这一段是 Flyway 踩坑重灾区。很多团队用了一阵 Flyway 后突然某天启动报错Migration checksum mismatch for migration version 2原因十有八九是有人修改了已经执行过的 V2 脚本。Flyway 会把脚本内容做 checksum 存到历史表你改了文件内容历史表里的 checksum 对不上它就拒绝执行避免“同一个版本在不同环境跑出不同结果”。正确的做法是如果线上 V2 已经跑过你需求变了永远不改 V2而是新建 V3 去补偿变更。比如 V2 里建了booking_order表漏了service_time字段不要回头去改 V2直接写一个 V3ALTER TABLE booking_order ADD COLUMN service_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;这样开发库会从 V1 开始顺序执行到最新生产库从当前版本接着往后跑所有环境最终结构一致。这也是 Why Flyway 的底层逻辑——数据库结构变更也要具备不可变历史和线性演进能力。2.3 多数据库场景下如何组织目录我的目录结构最终长这样src/main/resources/db/migration/ ├── common/ │ ├── R__init_district_hot_rank.sql │ └── V100__init_base_dict_data.sql ├── h2/ │ └── V1__create_core_tables.sql ├── mysql/ │ ├── V1__create_core_tables.sql │ └── V2__add_booking_order_index.sql └── postgresql/ └── V1__create_core_tables.sqlSpring Boot 通过 profile 加载不同位置的迁移脚本application-dev.yml配置 H2 场景spring: flyway: locations: classpath:db/migration/h2,classpath:db/migration/commonapplication-test.yml配置 PostgreSQL 场景spring: flyway: locations: classpath:db/migration/postgresql,classpath:db/migration/commonapplication-prod.yml配置 MySQL 场景spring: flyway: locations: classpath:db/migration/mysql,classpath:db/migration/common注意这里不是把三个厂商目录同时加载进同一个库而是通过 profile 选择当前环境对应的厂商目录再叠加公共目录。这保证了不同环境下相同版本号的文件不会互相干扰因为启动时根本看不到其它厂商目录。真正让我踩过坑的是厂商专用脚本和公共脚本的版本号规划。最开始我把公共数据脚本叫V1__init_dict.sql厂商目录里也有V1__create_chef_profile.sql启动时 Flyway 报错说存在两个 V1。后来定下规矩厂商目录用 1~99公共目录从 V100 开始这才彻底清净。2.4 跨库 SQL 写法尽量使用最小公分母哪怕分了厂商目录仍然会有一些 SQL 需要同时出现在多个环境的脚本里尤其是 public 目录。这时候写 SQL 就得克制尽量用标准 SQL 和跨库类型。我的几个实操经验数字类型用INTEGER、BIGINT、DECIMAL(p,s)这类标准类型少用TINYINT这种 MySQL 特色类型如果一定要布尔语义直接用BOOLEANMySQL 8 和 PostgreSQL、H2 都支持。字符串用VARCHAR(n)别用 MySQL 的TEXT或者 PostgreSQL 的TEXT去硬顶长度能确定的都定长。时间戳统一用TIMESTAMP或DATETIME尽量避免CURRENT_TIMESTAMP ON UPDATE这种 MySQL 独有写法。自增主键的差异很大这种就放在厂商脚本里处理不要塞到公共目录。MySQL 用AUTO_INCREMENTPostgreSQL 用GENERATED BY DEFAULT AS IDENTITYH2 两种都兼容但没必要考验兼容性。以建厨师档案表为例MySQL 厂商脚本CREATE TABLE chef_profile ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, user_id BIGINT NOT NULL COMMENT 用户ID, display_name VARCHAR(50) NOT NULL COMMENT 厨师昵称, service_city VARCHAR(64) NOT NULL COMMENT 服务城市, status INTEGER NOT NULL DEFAULT 0 COMMENT 状态0待审核1可预约2休息, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_service_city (service_city) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;PostgreSQL 厂商脚本CREATE TABLE chef_profile ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, user_id BIGINT NOT NULL, display_name VARCHAR(50) NOT NULL, service_city VARCHAR(64) NOT NULL, status INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_chef_profile_service_city ON chef_profile(service_city);两份脚本逻辑完全等价只是方言不同。看起来好像重复维护但它换来的是每个环境执行时都不需要猜 SQL 是不是兼容。多数据库配置的意义就在这不是消灭重复而是把不可控的“运行时兼容问题”前移到“编码时明确区分”。2.5 迁移脚本的职责边界写迁移脚本时还要忍住一种冲动不要想让一个脚本同时把建表、刷数据、发通知全做了。Flyway 脚本是在应用启动期的数据库迁移阶段执行的不适合做业务动作。我在这个预约系统里一度很自然地在迁移 SQL 中写了“更新厨师评分后调用外部接口通知”的需求——当然最终没这么做因为它牵涉到外部依赖。迁移脚本应当是幂等、无副作用、只和数据库打交道的。真要通知用户、刷新缓存、发消息到 Redis Stream 通知下游服务这套逻辑应该放到应用层等 Flyway 迁移完成后再由应用启动事件或回调触发。否则脚本执行失败会导致外部调用已经发出两边状态对不上。3. Spring Boot 2 多数据库与多数据源实操配置3.1 依赖引入Spring Boot 版本和 Flyway 模块的坑先说依赖。Spring Boot 2.7 版本管理里已经带了 Flyway 版本直接用即可dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdorg.flywaydb/groupId artifactIdflyway-core/artifactId /dependency如果你用的是 MySQLFlyway 8.2 以后把 MySQL 支持拆成了独立模块所以还要额外加一个依赖dependency groupIdorg.flywaydb/groupId artifactIdflyway-mysql/artifactId /dependency很多老项目从 Spring Boot 2.3、2.5 升级到 2.7 后突然报“No database found to handle”之类的错误多半就是缺了flyway-mysql。Spring Boot 2.7 的 BOM 里已经管理这个模块的版本直接引坐标即可。PostgreSQL、H2 的支持在flyway-core里本来就带。3.2 单数据源多环境 yml 配置示例如果你是一个应用只连一个库只是不同环境用不同数据库直接配置spring.flyway.locations就好。基础配置如下spring: flyway: enabled: true baseline-on-migrate: false clean-disabled: true locations: classpath:db/migration/xxx,classpath:db/migration/common一个一个参数解释enabledtrue表示应用启动时自动迁移。如果哪天你不想让某台机器启动时跑迁移比如临时排查问题可以用启动参数--spring.flyway.enabledfalse覆盖。baseline-on-migratefalse是针对已经存在的“非空但无 Flyway 管理”的数据库。如果第一次接入 Flyway 时库里已经有表了直接启动会报错提示 schema 非空。解决方式是把该参数设为trueFlyway 会在当前库打一个基线版本。一般首次接入时设一次之后保持 false。clean-disabledtrue是为了防止误执行clean清库。Flyway 的 clean 不是运维工具是开发期清理库用的生产环境千万别开。locations可以配多个用逗号分隔。H2 本地开发示例以兼容 MySQL 模式为例spring: datasource: url: jdbc:h2:file:./data/devdb;MODEMySQL;DATABASE_TO_LOWERTRUE;CASE_INSENSITIVE_IDENTIFIERSTRUE driver-class-name: org.h2.Driver username: sa password: flyway: locations: classpath:db/migration/h2,classpath:db/migration/commonMySQL 生产示例spring: datasource: url: jdbc:mysql://localhost:3306/cooking?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver username: prod_user password: prod_password flyway: locations: classpath:db/migration/mysql,classpath:db/migration/common这套配置一个应用跑多个环境没有问题代码也不用换。核心逻辑就是“厂商脚本目录跟着 profile 走公共脚本永远加载”。3.3 多数据源场景不要让自动配置把两个库都跑一遍现在很多服务会拆主库和从库或者按业务拆多个库比如一个订单库、一个用户库。这时麻烦就来了Spring Boot 的 Flyway 自动配置只针对一个DataSource。当你配置了多个数据源如果不做处理默认的 Flyway 可能会只连接那个被Primary标注的数据源去执行另一个库完全没有被管理或者你希望两个库各自管理但自动配置只准备了一份迁移脚本位置两个库都跑同一套脚本结果另一个库多了一堆不该存在的表。我在做过一次“配置中心多库”改造后果断放弃了默认自动配置处理多数据源的方式改成了手动注册 Flyway Bean。第一步在配置文件里把 Flyway 自动执行关掉spring: flyway: enabled: false第二步手动给每个数据源创建一个独立的 Flyway Bean每个都指定自己的迁移目录Configuration public class MultiDataSourceFlywayConfig { Bean(initMethod migrate) public Flyway orderFlyway(Qualifier(orderDataSource) DataSource orderDataSource) { return Flyway.configure() .dataSource(orderDataSource) .locations(classpath:db/migration/order) .baselineOnMigrate(false) .load(); } Bean(initMethod migrate) public Flyway userFlyway(Qualifier(userDataSource) DataSource userDataSource) { return Flyway.configure() .dataSource(userDataSource) .locations(classpath:db/migration/user) .baselineOnMigrate(false) .load(); } }这样两个数据源各自维护各自的迁移脚本和flyway_schema_history表。注意Flyway.configure().load()只是创建了一个 Flyway 实例真正执行要调用migrate()通过initMethod migrate让 Spring 在 Bean 初始化阶段触发。多数据源场景下Spring Boot 2 自动配置的 FlywayAutoConfiguration 看到你实现了自定义 Flyway Bean会通过条件注解让路不会重复执行。如果项目里既有主数据源又想保留 Spring Boot 自动配置另一个库需要避免被重复跑更清晰的还是把自动配置关掉全部自己管理。自动化带来的“方便”在多数据源场景下反而是风险。3.4 多实例部署时的执行顺序控制聊完多库再聊一个很多人忽略的“多实例”场景。生产环境为了高可用同一个服务可能会部署 2~3 个实例。如果所有实例都开启 Flyway 自动迁移同时启动时会并发执行 DDLFlyway 虽然会在 schema history 上做一些锁控制但不同数据库表现不一致而且 DDL 本身就不适合高并发竞争。我实践中的发布策略是新版本上线时先只启动一个“迁移实例”给它传--spring.flyway.enabledtrue其余实例先不启动或通过配置--spring.flyway.enabledfalse跳过自动迁移。等迁移实例执行完确认 schema history 里没有 failed 记录再滚动启动其余实例。如果迁移失败直接停发布流程先把 failed 记录处理掉再继续。有些团队会把 Flyway 集成到 CI/CD 里单独跑一步应用启动时完全不依赖自动迁移这也是一种更稳的方式。当然这会增加流水线复杂度小团队初期用“启动时迁移 发布窗口控制”就够了。3.5 把迁移状态暴露到 Actuator 和监控Spring Boot 的 Actuator 对 Flyway 有原生支持只要引入了spring-boot-starter-actuator并在配置里打开端点management: endpoints: web: exposure: include: health,flyway启动后请求/actuator/flyway能看到每个迁移脚本的版本、描述、类型、执行时间和 success 标记。这个端点对我们排查生产环境“脚本到底跑没跑”特别有用不用再登数据库查flyway_schema_history表。如果你已经接了 Micrometer完全可以把迁移结果做成业务指标。比如写一个定时任务定期扫一下关键库的 schema history 状态发现 failed 记录就记一个自研的flyway_migration_failed_total计数器再配合告警通知到群里。这类迁移失败往往出现在凌晨发布时段没人盯着很容易拖到白天用户反馈才被发现。健康检查也要留意。Spring Boot 的 db health indicator 检查的是数据库连通性Flyway 失败会导致应用启动失败吗分情况。如果你依赖自动迁移迁移抛异常会影响启动应用根本起不来但如果你的应用已经起来某个迁移是后续异步执行的那就要靠告警兜底。我一般建议让“迁移失败 启动失败”宁可发布失败回滚也不要带着半个 schema 硬跑。4. 常见问题与排查技巧实录4.1 校验和不匹配 Migration checksum mismatch这是 Flyway 使用中最常见的问题报错长这样Migration checksum mismatch for migration version 2我之前踩过最典型的一次某个同事觉得 V2 脚本里的注释写得不好直接把 SQL 文件里的注释改了结果所有已经跑过 V2 的环境全部启动失败。排查步骤先查库里的flyway_schema_history确认该版本在哪些环境已经执行过。判断是否真的需要让这个版本在后续环境重新执行。如果不需要改回原内容即可如果确实要变更就新建新版本脚本去补偿。如果只是开发库误改导致启动不了可以用flyway repair把历史表里的 checksum 重新校准。生产库尽量别用而是走版本递增的方式。4.2 库非空导致首次接入失败如果项目在某个环境已经有存量表了首次引入 Flyway 时会报Schema xxx is not empty说明这个库不是全新库Flyway 不知道这些表是哪个版本建的。解决方式是在配置里设置spring: flyway: baseline-on-migrate: true baseline-version: 1Flyway 会把当前库标记为 baseline 版本然后从后续版本开始执行。对于已有大量生产数据的库要谨慎处理 baseline 的版本号与后续脚本的连续性尽量保证从“当前实际结构”出发而不是从空库逻辑出发。4.3 多目录同时存在相同版本号当你配置了多个locations比如classpath:db/migration/common和classpath:db/migration/mysql两边都写了V1__xxx.sql启动时会报Found more than one migration with version 1Flyway 虽然是从多个 location 加载文件但版本号在同一个 schema history 里是全局唯一的。解决办法就是我在第 2.1 节说的版本号规划厂商目录占 1~99公共目录占 100 以后或者干脆把厂商脚本和公共脚本做成完全不同的前缀语义避免在公共区间撞车。4.4 本地 H2 能过、生产 MySQL 崩这算多数据库配置中最容易炸的问题也是我写这篇文章的核心出发点之一。有次我们在本地 H2 上写了一个 SQLALTER TABLE booking_order ADD COLUMN tags TEXT;H2 对TEXT兼容得很好但 MySQL 的TEXT不能有默认值后续插入和查询都出现怪问题还有一次在 PostgreSQL 测试环境用了JSONB生产是 MySQL 5.7根本没有这个类型。这类问题不能全指望写“跨库通用 SQL”解决没那个银弹。我后来的做法是所有具备方言倾向的字段定义全部进厂商目录只有通用 DML 才放 common 目录。另外本地开发可以把 H2 的兼容模式开到和测试环境接近比如MODEPostgreSQL或者MODEMySQL能提前暴露一部分类型问题。解决不了的那部分靠 Testcontainers 跑真实数据库来兜底。如果项目 CI 不跑真实数据库测试那多数据库配置做得再漂亮也只是把问题延后。4.5 DDL 隐式提交导致事务不可回滚Flyway 默认会把每个迁移脚本包在事务里执行但很多数据库的 DDL 语句会隐式提交事务。比如 MySQL 的ALTER TABLE、CREATE TABLE一旦执行就可能把当前事务提交掉。这意味着一个脚本里如果前半段 DDL 成功了、后半段 DML 失败了Flyway 回滚时可能只回滚了后半段前半段已经提交状态变得“半新不旧”。千万不要以为 Flyway 会自动保证脚本原子性。我建议一个迁移脚本尽量只做一种类型的操作。要么专做 DDL要么专做 DML如果确实需要在一个版本里改表并刷数据比如先加列再回填值要自己评估失败后可重试性给生产环境做迁移时提前备份或至少导出当前结构出问题才有退路。4.6 多数据源时“另一个库没跑迁移”这个问题我印象最深因为排查了很久。应用配了两个数据源日志里只看到一个 Flyway 执行记录另一个库完全是空的。原因就是我前面提到的Spring Boot 的 Flyway 自动配置通常只认一个DataSource拿到的可能是Primary数据源。多数据源场景你要么手动注册多个 Flyway Bean要么用FlywayConfigurationCustomizer按数据源分别定制。不要试图在application.yml里通过多段spring.flyway配置解决多数据源问题Spring Boot 原生不支持那种写法。4.7 迁移脚本带外部副作用导致重放问题Flyway 脚本天然可能被重复执行多次比如开发环境每次重建库都要从 V1 跑一遍。如果你在脚本里写了外部 HTTP 调用、写 Redis、发消息就一定会在重放时产生脏数据或者重复通知。我在预约系统里做过一个反面尝试想在“新增订单评价表”后顺便调用推送服务通知厨师。后来改成在应用层监听数据库迁移完成事件由 Java 代码统一发通知才把“数据库变更”和“业务通知”彻底解耦。如果你的业务确实需要“结构变更后通知下游”建议顺序是Flyway 迁移脚本只负责数据库应用启动后通过ApplicationRunner或自定义回调检查当前 schema 版本如果达到某个版本再触发缓存刷新、Redis Stream 消息发布等动作。这样即使消息发送失败也不会污染数据库迁移的校验和记录。5. 一些值得长期保留的习惯最后聊几个我被问过很多次、也自己反复验证过的实践细节。第一个尽量在代码里显式关闭 Hibernate 的自动建表。如果你的应用同时引入了 JPA一定要把spring.jpa.hibernate.ddl-auto设为none或validate不要设成update。我第一次把 Flyway 接入 JPA 项目时忘了改这项配置结果每次启动 Hibernate 都尝试把实体对应表结构“微调”一遍和 Flyway 管理的 schema 打架校验和倒是没坏但表结构经常出现意想不到的字段。记住数据库结构只有 Flyway 一个入口JPA 负责实体映射和查询别让它抢着改库。第二个迁移脚本要做到“人肉可解释”。团队里不是每个人都会细看 Flyway 文档。我后来在新人入职时会直接告诉他们一个简单的审查标准如果你看到某个 SQL 文件不能在三句话内说清它做了什么、为什么需要它、能不能安全回滚那这个脚本就不要合入主干。保持良好的脚本命名和描述习惯比任何文档都管用。第三个多数据库配置不是越多越好。如果你的项目压根没有多环境差异一个 MySQL 一个目录就够了。硬上“厂商目录 公共目录”反而增加维护成本。我给的这套方案是针对 H2/PostgreSQL/MySQL 并存这类真实场景的如果你们生产、测试、开发全是同一个数据库完全可以简化成单目录。工具是为人服务的别为了“规范”而规范。第四个后续扩展方向。Spring Boot 3.x 已经切到 Jakarta 命名空间Flyway 的版本也一路升级到了 9/10。底层 API 大体相似但依赖坐标和一些配置项有调整。如果你是在新项目里用 Spring Boot 3建议直接看官方迁移指南别把老项目的依赖硬搬过去。本文讨论的目录组织、版本号规划和多库隔离思路仍然适用只是版本细节需要跟着官方更新。回到开头那句话数据库迁移这事本质上是个工程规范问题不是工具问题。Flyway 把我从“谁会记得手动执行 SQL”的焦虑里解放出来但它真正发挥价值的前提是团队愿意遵守一套规则脚本不可变、版本号有序、多环境按目录隔离、结构变更经过评审。如果你已经在上手 Flyway 或者正在被多数据库环境折磨不妨从今天开始先把目录结构和命名规范定下来后面会省下大量“救火”时间。
返回列表