3. Flyway 迁移执行模型:迁移执行流程、校验机制、锁机制与集群环境下的行为
在生产环境中,Flyway 的迁移执行并非简单的 SQL 文件顺序执行。理解其内部执行模型——包括执行流程、校验机制、锁机制以及集群环境下的协作行为——是构建可靠异步任务恢复与重试策略的基础。本章将深入剖析 Flyway 的核心执行引擎,揭示其在单机与集群模式下的运行细节。
3.1 迁移执行核心流程
Flyway 的迁移执行遵循一个严格的生命周期,从连接数据库到应用迁移,每一步都包含状态检查与版本控制。下图展示了 Flyway 执行一次迁移的完整流程:
上述流程中的每一步都至关重要。其中,步骤 2(锁定)和步骤 3(校验)是确保迁移一致性的关键防线,也是异步任务恢复与重试策略需要重点关注的环节。
3.2 校验机制:Checksum 与一致性保障
Flyway 的校验机制用于检测已应用迁移是否被意外修改。每次迁移执行后,Flyway 会计算迁移文件的 checksum(校验和)并记录在 flyway_schema_history 表中。当再次运行迁移时,Flyway 会重新计算已应用迁移文件的 checksum,并与数据库中的记录进行比对。
| 校验场景 | 行为描述 | 对重试策略的影响 |
|---|---|---|
| Checksum 匹配 | 迁移文件未被修改,校验通过,继续执行后续流程。 | 正常执行,无需特殊处理。 |
| Checksum 不匹配 | 迁移文件内容与历史记录不一致,Flyway 抛出 FlywayException,迁移失败。 |
重试前必须修复迁移文件或手动更新 checksum 记录,否则重试将反复失败。 |
| 迁移文件缺失 | 历史记录中存在但本地文件系统中找不到对应迁移文件,Flyway 抛出异常。 | 重试前需要恢复缺失的迁移文件,或手动清理历史记录。 |
INSERT INTO 而非 INSERT IGNORE),即使 checksum 校验通过,重试执行也可能导致数据重复或主键冲突。因此,迁移脚本的设计必须遵循幂等性原则,或重试逻辑需要配合事务回滚。
3.3 锁机制:集群环境下的排他执行
在集群或多实例部署环境中,多个应用实例可能同时尝试执行数据库迁移。为了防止迁移冲突和数据不一致,Flyway 引入了锁机制。Flyway 使用数据库层面的表锁或行锁来实现排他访问。
3.3.1 锁的类型与行为
| 数据库类型 | 锁实现方式 | 锁范围 | 超时行为 |
|---|---|---|---|
| MySQL | GET_LOCK() 命名锁 |
会话级别 | 默认等待 120 秒,超时抛出异常 |
| PostgreSQL | pg_advisory_lock | 会话级别 | 阻塞等待,无超时(可配置) |
| Oracle | DBMS_LOCK 包 | 会话级别 | 可配置超时 |
| SQL Server | app_lock 存储过程 | 事务/会话级别 | 可配置超时 |
3.3.2 锁的获取与释放流程
- 尝试获取锁: 在迁移开始前,Flyway 尝试获取一个与数据库 URL 和 schema 名称关联的独占锁。
- 锁等待与超时: 如果锁已被其他实例持有,当前实例进入等待状态。等待时间由
flyway.lockRetryCount和flyway.lockRetryInterval参数控制。 - 锁持有: 成功获取锁的实例执行完整的迁移流程(校验、执行、更新历史表)。
- 锁释放: 迁移完成后,无论成功或失败,锁都会被释放。如果迁移失败,锁释放后其他实例可以尝试重试。
3.4 集群环境下的行为与协作
在集群环境中,Flyway 的默认行为是“谁先拿到锁,谁执行迁移”。其他实例在锁释放后,会重新执行校验流程,但不会重复执行已经成功应用的迁移。下图展示了两个应用实例在集群环境下的迁移协作过程:
3.4.1 集群环境下的关键行为总结
- 单点执行: 同一时刻只有一个实例能执行迁移,其他实例等待或跳过。
- 自动跳过: 锁释放后,等待的实例会重新校验
flyway_schema_history表,自动跳过已成功应用的迁移。 - 失败回滚: 如果执行迁移的实例在迁移过程中崩溃,锁会在数据库会话超时后自动释放。其他实例获取锁后,会检测到未完成的迁移(状态为
pending或failed),并根据配置决定是否重试。 - 配置参数:
flyway.outOfOrder(允许乱序迁移)、flyway.ignoreMissingMigrations(忽略缺失迁移)等参数在集群环境下需要谨慎配置,以避免实例间状态不一致。
3.5 迁移状态机与错误恢复点
Flyway 在 flyway_schema_history 表中记录了每次迁移的状态。理解这些状态对于设计异步任务恢复策略至关重要。
| 状态 | 描述 | 重试可行性 |
|---|---|---|
| Pending | 迁移已发现但尚未执行。 | 可直接重试,Flyway 会正常执行。 |
| Success | 迁移已成功应用。 | 无需重试,Flyway 会自动跳过。 |
| Failed | 迁移执行过程中发生错误,事务已回滚(如果数据库支持 DDL 事务)。 | 可重试,但需要确保迁移脚本是幂等的,或手动清理部分执行产生的副作用。 |
| Ignored | 迁移被用户手动忽略(通常用于修复失败的迁移)。 | 重试不会执行,需要手动修改状态为 Pending 才能重新执行。 |
| Missing | 历史记录中存在但本地文件系统中找不到对应的迁移文件。 | 重试前需要恢复文件或清理历史记录。 |
在异步任务恢复场景中,Failed 状态是最常见的重试触发点。Flyway 默认不会自动重试失败的迁移,需要外部任务调度系统(如 Quartz、Spring Retry 或 Kubernetes Job)来监控状态并触发重试。
- 幂等性优先: 所有迁移脚本应设计为可重复执行,例如使用
CREATE TABLE IF NOT EXISTS、ALTER TABLE ... ADD COLUMN IF NOT EXISTS(PostgreSQL 9.6+)或先检查再执行。 - 事务性 DDL: 优先使用支持事务性 DDL 的数据库(如 PostgreSQL),这样迁移失败时可以自动回滚,避免残留状态。
- 重试次数限制: 设置最大重试次数(如 3 次),超过后标记为人工介入,避免无限重试导致资源浪费。
- 健康检查: 在重试前检查数据库连接和锁状态,避免在数据库不可用时盲目重试。