2、Flyway工作原理:元数据表、版本号解析与锁定机制
Flyway 的核心设计围绕“版本化数据库迁移”展开。每一次对数据库结构的变更(DDL/DML)都被封装为一个迁移脚本,并由 Flyway 按照版本号顺序依次执行。为了确保迁移过程的可追溯、幂等与并发安全,Flyway 依赖一套严谨的元数据管理、版本号解析与锁定机制。本章将深入剖析这三个核心环节。
2.1 Flyway 元数据表:flyway_schema_history
Flyway 在首次执行迁移时,会自动在目标 Schema 中创建一张名为 flyway_schema_history 的元数据表。这张表是 Flyway 的“大脑”,记录了所有已执行迁移的完整历史。每次迁移执行后,Flyway 都会向该表插入一条记录;每次启动时,Flyway 都会读取该表以确定当前数据库状态。
2.1.1 表结构定义
下表列出了 flyway_schema_history 的核心字段及其语义:
| 字段名 | 数据类型 | 说明 |
|---|---|---|
installed_rank |
INTEGER | 执行顺序序号,从 1 开始递增,用于确定迁移的执行顺序 |
version |
VARCHAR(50) | 迁移版本号,如 1、2.1、20250101.1;可重复迁移(Repeatable)该字段为 NULL |
description |
VARCHAR(200) | 迁移脚本的描述信息,取自文件名中的描述部分 |
type |
VARCHAR(20) | 迁移类型:SQL、JDBC、SPRING_JDBC 等 |
script |
VARCHAR(1000) | 迁移脚本的完整文件名,如 V2__add_users.sql |
checksum |
INTEGER | 脚本内容的校验和(CRC32),用于检测脚本是否被篡改 |
installed_by |
VARCHAR(100) | 执行迁移的数据库用户 |
installed_on |
TIMESTAMP | 迁移执行完成的时间戳 |
execution_time |
INTEGER | 迁移执行耗时,单位为毫秒 |
success |
BOOLEAN | 执行是否成功:1 表示成功,0 表示失败 |
flyway_schema_history 表应被纳入日常备份策略。一旦该表损坏或丢失,Flyway 将无法感知当前数据库的版本状态,可能导致重复迁移或数据丢失。
2.2 迁移版本号解析机制
Flyway 通过文件名解析迁移版本号,并依据版本号决定执行顺序。版本号解析遵循一套明确的规则,理解这些规则是避免迁移冲突的前提。
2.2.1 文件命名规范
Flyway 迁移脚本的文件名由三部分组成:
前缀 + 版本号 + 分隔符 + 描述 + 后缀
示例:
V1__init_schema.sql
V2.1__add_user_table.sql
V2025_01_01__create_index.sql
R__daily_cleanup.sql (Repeatable 迁移,无版本号)
- 前缀:
V表示版本化迁移(Versioned),R表示可重复迁移(Repeatable)。 - 版本号:由数字和点(
.)或下划线(_)组成,如1、2.1、2025_01_01。 - 分隔符:双下划线
__,用于分隔版本号和描述。 - 描述:简短说明,单词间可用下划线或空格。
- 后缀:
.sql或.java等。
2.2.2 版本号排序规则
Flyway 将版本号视为由数字和点分隔的多段数值,按从左到右逐段比较的方式排序:
| 版本号 | 解析后的数值数组 | 排序位置 |
|---|---|---|
1 |
[1] | 1 |
1.1 |
[1, 1] | 2 |
1.2 |
[1, 2] | 3 |
2 |
[2] | 4 |
2.0.1 |
[2, 0, 1] | 5 |
10 |
[10] | 6 |
10 会被解析为数值 10,排在 9 之后。不要使用前导零(如 01),因为 Flyway 会将其解析为 1,导致排序混乱。推荐使用语义化版本号,如 1.0.0、2.1.3。
2.3 校验与锁定机制
Flyway 通过校验(Checksum)和锁定(Lock)两大机制,确保迁移的完整性与并发安全性。
2.3.1 校验机制:防止脚本篡改
每次迁移执行后,Flyway 会计算脚本内容的 CRC32 校验和,并存入 flyway_schema_history.checksum 字段。下次启动时,Flyway 会重新计算已执行脚本的校验和,并与数据库记录比对:
- 校验通过:脚本未被修改,继续执行后续迁移。
- 校验失败:抛出
FlywayException,阻止应用启动,防止因脚本篡改导致的数据不一致。
R__)每次执行前都会重新计算校验和,若发现变化则重新执行。
2.3.2 锁定机制:防止并发冲突
在微服务或多实例部署场景下,多个应用实例可能同时启动并尝试执行迁移。Flyway 使用数据库级别的表锁来保证同一时刻只有一个实例执行迁移操作。
锁定流程如下:
- 实例 A 尝试获取
flyway_schema_history表的写锁。 - 若获取成功,实例 A 开始执行迁移;其他实例(B、C)等待锁释放。
- 实例 A 迁移完成后释放锁。
- 实例 B 获取锁后,发现数据库版本已是最新,跳过迁移直接启动应用。
下图展示了 Flyway 从启动到完成迁移的完整工作流程,包含校验与锁定环节:
2.4 关键机制对比总结
下表从目的、触发时机、失败影响三个维度对比校验与锁定机制:
| 机制 | 目的 | 触发时机 | 失败影响 |
|---|---|---|---|
| 校验(Checksum) | 检测已执行脚本是否被篡改 | 每次应用启动时 | 应用启动失败,需人工修复脚本或手动更新 checksum |
| 锁定(Lock) | 防止多实例并发执行迁移 | 迁移执行前获取锁,执行后释放 | 锁等待超时后抛出异常,需检查其他实例状态 |
flyway.lock-retry-count 和 flyway.lock-retry-interval 调整锁重试策略,避免因短暂锁冲突导致任务失败。默认重试次数为 1 次,间隔为 1 秒。