Postgresql 创建架构(如果不存在)将引发重复键错误
为了提供一些上下文,该命令是在任务内部发出的,许多任务可能同时从多个工作人员发出相同的命令 每个任务都尝试创建postgres模式。我经常遇到以下错误: Postgres版本是PostgreSQL 9.4rc1。Postgresql 创建架构(如果不存在)将引发重复键错误,postgresql,concurrency,ddl,catalog,Postgresql,Concurrency,Ddl,Catalog,为了提供一些上下文,该命令是在任务内部发出的,许多任务可能同时从多个工作人员发出相同的命令 每个任务都尝试创建postgres模式。我经常遇到以下错误: Postgres版本是PostgreSQL 9.4rc1。 这是Postgres中的一个bug吗?这是表和模式的实现中的一个小缺点。基本上,它们是一种颠覆性的尝试,PostgreSQL不能干净地处理竞争条件。它是安全的,但很难看 如果架构正在另一个会话中并发创建,但尚未提交,那么它既存在也不存在,具体取决于您是谁以及您的外观。其他事务不可能在系
这是Postgres中的一个bug吗?这是表和模式的
实现中的一个小缺点。基本上,它们是一种颠覆性的尝试,PostgreSQL不能干净地处理竞争条件。它是安全的,但很难看
如果架构正在另一个会话中并发创建,但尚未提交,那么它既存在也不存在,具体取决于您是谁以及您的外观。其他事务不可能在系统目录中“看到”新架构,因为它是未提交的,所以它在pg_namespace
中的条目对其他事务不可见。因此,createschema
/createtable
尝试创建它,因为就它而言,对象不存在
但是,这会将一行插入具有唯一约束的表中。唯一约束必须能够看到未提交的行才能正常工作。因此,insert阻塞(停止)直到执行创建的第一个事务提交或回滚。如果提交,第二个事务将中止,因为它试图插入违反唯一约束的行<代码>创建架构
不够聪明,无法捕获此案例并重试
要正确修复此PostgreSQL,可能需要谓词锁定,因为它可以锁定行的潜力。这可能会被添加为当前正在进行的实现UPSERT
的工作的一部分
对于这些特定的命令,PostgreSQL可能会对系统目录进行脏读,从而可以看到未提交的更改。然后它可以等待未提交的事务提交或回滚,重新执行脏读以查看是否有其他人在等待,然后重试。但是这会有一个竞争条件,在这个竞争条件下,其他人可能会在您进行读取以检查模式和尝试创建模式之间创建模式
因此,如果不存在
变体必须:
- 检查模式是否存在;如果有,就什么也不做就结束
- 尝试创建表
- 如果由于唯一约束错误导致创建失败,请在开始时重试
- 如果表创建成功,请完成
据我所知,没有人实施过,或者他们尝试过,但没有被接受。使用这种方法可能会出现事务ID烧录率等问题
我认为这是一种缺陷,但它是一种“是的,我们知道”的缺陷,而不是一种“我们会马上修复”的缺陷。请随意向pgsql发布关于它的错误;如果不存在,文档至少应该提到这一警告
我不建议像那样并发地执行DDL。我需要在同时创建模式的应用程序中解决这个限制。对我有效的是增加
LOCK TABLE pg_catalog.pg_namespace
在事务中,包括创建架构。看起来这是一件肮脏且不安全的事情,但却帮助我解决了仅在测试中出现的问题。似乎IF EXISTS仅在提交后才起作用,而不是在尚未提交的事务期间。CREATE SCHEMA
自Postgres 9.3起就有了IF not EXISTS
子句,所以我不认为你的9.4rc1版本和它有任何关系。但是您仍然应该。很好,但是我的用户没有锁定pg_catalog.pg_namespace
模式存在之前,我还可以锁定什么?
LOCK TABLE pg_catalog.pg_namespace