1. Flyway与PostgreSQL并发索引创建的死锁问题解析
作为一名长期使用Flyway进行数据库迁移的开发者,我遇到过最棘手的问题之一就是flyway migrate命令在PostgreSQL环境下无限等待的情况。这个问题通常发生在使用CONCURRENTLY关键字创建索引时,表面看起来像是Flyway死锁了,但实际上背后有着更深层次的原因。
1.1 问题现象与初步排查
当这个问题发生时,你会观察到以下典型现象:
flyway migrate命令执行后长时间卡住,没有任何进展- 数据库服务器CPU和IO使用率异常低
- 应用日志中没有任何错误信息
- PostgreSQL服务端日志也没有异常记录
这种情况特别容易发生在以下场景中:
- 你的迁移脚本中包含使用
CONCURRENTLY创建索引的语句 - 你有多个这样的迁移脚本连续执行
- 这些迁移脚本都被标记为非事务性执行(
executeInTransaction=false)
1.2 为什么会出现无限等待?
要理解这个问题的根源,我们需要深入分析Flyway和PostgreSQL的工作机制:
Flyway的锁机制:
Flyway在执行迁移时会获取schema锁,确保同一时间只有一个迁移进程可以操作数据库结构。这个锁会一直保持到整个迁移批次完成。
PostgreSQL的CONCURRENTLY机制:
当使用CONCURRENTLY创建索引时,PostgreSQL需要等待所有正在进行的事务完成才能开始索引构建。这是一种避免表锁的设计,允许在索引构建期间继续读写数据。
问题产生的关键:
当第一个迁移脚本创建索引完成后,Flyway不会立即释放数据库连接。此时如果第二个迁移脚本也开始创建索引,它会:
- 等待Flyway的schema锁(由第一个迁移持有)
- 同时PostgreSQL会等待所有活动事务完成(包括第一个迁移未释放的连接)
这就形成了一个典型的死锁局面,导致第二个迁移脚本无限期等待。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题根源的技术分析
2.1 Flyway 9.x的锁机制变化
Flyway 9.x版本对锁机制做了重要调整,这也是这个问题变得普遍的原因之一。新版本的Flyway:
- 采用更严格的锁策
