我正在开发启动多个并发客户端以连接到 PostgreSQL (12) 数据库的软件。当每个客户端启动时,它在连接到 PostgreSQL 时做的第一件事就是运行模式创建脚本。
这个脚本是幂等的——至少在原则上——这样多个客户端不应该绊倒自己。总的来说,这很好用。但是,PostgreSQL 有时会检测到死锁和受影响的客户端崩溃。查看日志记录,我相信这些是按照这样的顺序发生的:
- 客户端 A:开始架构创建事务
- 客户端 A:完成模式创建事务
- 客户端 B:开始架构创建事务
- 客户端 A:使用模式的新事务(从视图中选择)
- 客户端 A 和 B 现在处于死锁状态
日志不是 100% 清晰的,我无法确定地重现这一点,但这似乎是正在发生的事情:客户端 A 正在尝试SELECT
从架构定义的视图中进行操作,但由于客户端 B 正在尝试重新创建该视图,因此出现了死锁模式脚本中的视图 ( CREATE OR REPLACE VIEW
)。
是否可以确保模式创建脚本独占运行?或者,是否有其他解决方案(例如,而不是CREATE OR REPLACE VIEW
,我只有CREATE VIEW
在我确定它不存在之后)?
根据@DanielVérité,从对我的问题的评论中,可以将问题概括为“并发DDL 很糟糕;并发DDL 和DML 真的很糟糕”。我没有意识到这一点——即使我小心翼翼地使 DDL 幂等——但知道这是一件好事。所以,这给我留下了两个广泛的解决方案:
不要同时执行 DDL(例如,让单个客户端实例化模式)。
从应用程序开发的角度来看,这可能是“最简单”的解决方案——我将其视为我的备份计划——但它会对应用程序的架构产生影响。如果必须,我只想走这条路。
根据@a_horse_with_no_name 的建议使用咨询锁。
这只需要对我的应用程序的数据库库进行少量修改,因此不需要对架构进行大的架构更改或更改。它的缺点是我必须获取和释放每个事务的锁(以确保它没有被代码的 DDL 部分锁定),这必然会跨客户端序列化所有事务。
我正在尝试选项 2,它似乎正在工作。我需要再运行几次以说服自己死锁已经消失以及序列化对性能的影响(有趣的是,到目前为止,它可以忽略不计)。当我有结果时,我会报告结果......
当您尝试创建架构(又名 DDL)命令咨询锁时,我认为不会起作用
我能想到的最佳解决方案是创建一个表来跟踪架构创建或直接查询 pg_catalog 然后将此调用包装到一个过程中
通过一个程序,您可以开始并提交事务
这是我的思考过程,尚未经过测试
接下来只需调用此过程